From 4073028188d2cfe4be4106f370fd94a38f7301bc Mon Sep 17 00:00:00 2001 From: Ben Vanik Date: Sat, 25 May 2013 20:32:58 -0700 Subject: [PATCH] Very basic, super slow, nasty indirection. --- src/xenia/cpu/global_exports.cc | 10 +++-- src/xenia/cpu/global_exports.h | 2 +- src/xenia/cpu/jit.h | 1 + src/xenia/cpu/processor.cc | 61 +++++++++++++++++++--------- src/xenia/cpu/processor.h | 2 + src/xenia/cpu/sdb/symbol_database.cc | 37 ++++------------- src/xenia/cpu/sdb/symbol_table.cc | 11 ++++- src/xenia/cpu/sdb/symbol_table.h | 2 + src/xenia/cpu/x64/x64_emitter.cc | 28 +++++++++++-- src/xenia/cpu/x64/x64_jit.cc | 21 +++++++--- src/xenia/cpu/x64/x64_jit.h | 1 + 11 files changed, 113 insertions(+), 63 deletions(-) diff --git a/src/xenia/cpu/global_exports.cc b/src/xenia/cpu/global_exports.cc index 5a56e9036..36c0354ec 100644 --- a/src/xenia/cpu/global_exports.cc +++ b/src/xenia/cpu/global_exports.cc @@ -9,6 +9,7 @@ #include +#include #include #include #include @@ -30,11 +31,12 @@ void _cdecl XeTrap( XEASSERTALWAYS(); } -void _cdecl XeIndirectBranch( +void* _cdecl XeIndirectBranch( xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) { - XELOGCPU("INDIRECT BRANCH %.8X -> %.8X", - (uint32_t)br_ia, (uint32_t)target); - XEASSERTALWAYS(); + // TODO(benvanik): track this statistic - this path is very slow! + Processor* processor = (Processor*)state->processor; + void* target_ptr = processor->GetFunctionPointer((uint32_t)target); + return target_ptr; } void _cdecl XeInvalidInstruction( diff --git a/src/xenia/cpu/global_exports.h b/src/xenia/cpu/global_exports.h index 561adec4b..5c787c3de 100644 --- a/src/xenia/cpu/global_exports.h +++ b/src/xenia/cpu/global_exports.h @@ -25,7 +25,7 @@ namespace cpu { typedef struct { void (_cdecl *XeTrap)( xe_ppc_state_t* state, uint64_t cia); - void (_cdecl *XeIndirectBranch)( + void* (_cdecl *XeIndirectBranch)( xe_ppc_state_t* state, uint64_t target, uint64_t br_ia); void (_cdecl *XeInvalidInstruction)( xe_ppc_state_t* state, uint64_t cia, uint64_t data); diff --git a/src/xenia/cpu/jit.h b/src/xenia/cpu/jit.h index b9dfbde5c..631f1a216 100644 --- a/src/xenia/cpu/jit.h +++ b/src/xenia/cpu/jit.h @@ -34,6 +34,7 @@ public: virtual int InitModule(ExecModule* module) = 0; virtual int UninitModule(ExecModule* module) = 0; + virtual void* GetFunctionPointer(sdb::FunctionSymbol* fn_symbol) = 0; virtual int Execute(xe_ppc_state_t* ppc_state, sdb::FunctionSymbol* fn_symbol) = 0; diff --git a/src/xenia/cpu/processor.cc b/src/xenia/cpu/processor.cc index ec6a69f7e..fbf644002 100644 --- a/src/xenia/cpu/processor.cc +++ b/src/xenia/cpu/processor.cc @@ -185,27 +185,12 @@ void Processor::DeallocThread(ThreadState* thread_state) { } int Processor::Execute(ThreadState* thread_state, uint32_t address) { - // Attempt to grab the function symbol from the global lookup table. - FunctionSymbol* fn_symbol = sym_table_->GetFunction(address); + // Attempt to get the function. + FunctionSymbol* fn_symbol = GetFunction(address); if (!fn_symbol) { - // Search all modules for the function symbol. - // Each module will see if the address is within its code range and if the - // symbol is not found (likely) it will do analysis on it. - // TODO(benvanik): make this more efficient. Could use a binary search or - // something more clever. - sdb::FunctionSymbol* fn_symbol = NULL; - for (std::vector::iterator it = modules_.begin(); - it != modules_.end(); ++it) { - fn_symbol = (*it)->FindFunctionSymbol(address); - if (fn_symbol) { - break; - } - } - if (!fn_symbol) { - // Symbol not found in any module. - XELOGCPU("Execute(%.8X): failed to find function", address); - return NULL; - } + // Symbol not found in any module. + XELOGCPU("Execute(%.8X): failed to find function", address); + return 1; } xe_ppc_state_t* ppc_state = thread_state->ppc_state(); @@ -230,3 +215,39 @@ uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address, } return ppc_state->r[3]; } + +FunctionSymbol* Processor::GetFunction(uint32_t address) { + // Attempt to grab the function symbol from the global lookup table. + FunctionSymbol* fn_symbol = sym_table_->GetFunction(address); + if (fn_symbol) { + return fn_symbol; + } + + // Search all modules for the function symbol. + // Each module will see if the address is within its code range and if the + // symbol is not found (likely) it will do analysis on it. + // TODO(benvanik): make this more efficient. Could use a binary search or + // something more clever. + for (std::vector::iterator it = modules_.begin(); + it != modules_.end(); ++it) { + fn_symbol = (*it)->FindFunctionSymbol(address); + if (fn_symbol) { + return fn_symbol; + } + } + + // Not found at all? That seems wrong... + XEASSERTALWAYS(); + return NULL; +} + +void* Processor::GetFunctionPointer(uint32_t address) { + // Attempt to get the function. + FunctionSymbol* fn_symbol = GetFunction(address); + if (!fn_symbol) { + return NULL; + } + + // Grab the pointer. + return jit_->GetFunctionPointer(fn_symbol); +} diff --git a/src/xenia/cpu/processor.h b/src/xenia/cpu/processor.h index 53807cc21..079e0c3e9 100644 --- a/src/xenia/cpu/processor.h +++ b/src/xenia/cpu/processor.h @@ -48,6 +48,8 @@ public: void DeallocThread(ThreadState* thread_state); int Execute(ThreadState* thread_state, uint32_t address); uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0); + sdb::FunctionSymbol* GetFunction(uint32_t address); + void* GetFunctionPointer(uint32_t address); private: xe_memory_ref memory_; diff --git a/src/xenia/cpu/sdb/symbol_database.cc b/src/xenia/cpu/sdb/symbol_database.cc index eddcb1b73..04b8217e8 100644 --- a/src/xenia/cpu/sdb/symbol_database.cc +++ b/src/xenia/cpu/sdb/symbol_database.cc @@ -448,18 +448,6 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) { // TODO(benvanik): queue for add without expensive locks? sym_table_->AddFunction(fn->start_address, fn); - XELOGSDB("Finished analyzing %.8X", fn->start_address); - return 0; -} - -int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) { - // Find variable accesses. - // TODO(benvanik): data analysis to find variable accesses. - - // A list of function targets that were undefined. - // This will run another analysis pass and it'd be best to avoid this. - std::vector new_fns; - // For each basic block: // - find outgoing target block or function for (std::map::iterator it = fn->blocks.begin(); @@ -485,26 +473,19 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) { // Function call. block->outgoing_type = FunctionBlock::kTargetFunction; block->outgoing_function = GetFunction(block->outgoing_address); - if (block->outgoing_function) { - FunctionSymbol::AddCall(fn, block->outgoing_function); - } else { - XELOGE("call target not found: %.8X -> %.8X", - block->end_address, block->outgoing_address); - new_fns.push_back(block->outgoing_address); - } + XEASSERTNOTNULL(block->outgoing_function); + FunctionSymbol::AddCall(fn, block->outgoing_function); } } } - if (new_fns.size()) { - XELOGW("Repeat analysis required to find %d new functions", - (uint32_t)new_fns.size()); - for (std::vector::iterator it = new_fns.begin(); - it != new_fns.end(); ++it) { - GetOrInsertFunction(*it, fn); - } - return 1; - } + XELOGSDB("Finished analyzing %.8X", fn->start_address); + return 0; +} + +int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) { + // Find variable accesses. + // TODO(benvanik): data analysis to find variable accesses. return 0; } diff --git a/src/xenia/cpu/sdb/symbol_table.cc b/src/xenia/cpu/sdb/symbol_table.cc index 4d9100954..c258d0c01 100644 --- a/src/xenia/cpu/sdb/symbol_table.cc +++ b/src/xenia/cpu/sdb/symbol_table.cc @@ -16,17 +16,26 @@ using namespace xe::cpu::sdb; SymbolTable::SymbolTable() { + lock_ = xe_mutex_alloc(10000); + XEASSERTNOTNULL(lock_); } SymbolTable::~SymbolTable() { + xe_mutex_free(lock_); + lock_ = NULL; } int SymbolTable::AddFunction(uint32_t address, FunctionSymbol* symbol) { + xe_mutex_lock(lock_); map_[address] = symbol; + xe_mutex_unlock(lock_); return 0; } FunctionSymbol* SymbolTable::GetFunction(uint32_t address) { + xe_mutex_lock(lock_); FunctionMap::const_iterator it = map_.find(address); - return it != map_.end() ? it->second : NULL; + FunctionSymbol* result = it != map_.end() ? it->second : NULL; + xe_mutex_unlock(lock_); + return result; } diff --git a/src/xenia/cpu/sdb/symbol_table.h b/src/xenia/cpu/sdb/symbol_table.h index b3df9ec99..fc7da6e81 100644 --- a/src/xenia/cpu/sdb/symbol_table.h +++ b/src/xenia/cpu/sdb/symbol_table.h @@ -29,6 +29,8 @@ public: sdb::FunctionSymbol* GetFunction(uint32_t address); private: + // TODO(benvanik): replace with a better data structure. + xe_mutex_t* lock_; typedef std::tr1::unordered_map FunctionMap; FunctionMap map_; }; diff --git a/src/xenia/cpu/x64/x64_emitter.cc b/src/xenia/cpu/x64/x64_emitter.cc index 3cc9c0c47..6e4517748 100644 --- a/src/xenia/cpu/x64/x64_emitter.cc +++ b/src/xenia/cpu/x64/x64_emitter.cc @@ -450,10 +450,21 @@ void X64Emitter::GenerateSharedBlocks() { SpillRegisters(); X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch); call->setPrototype(kX86FuncConvDefault, - FuncBuilder3()); + FuncBuilder3()); call->setArgument(0, c.getGpArg(0)); call->setArgument(1, locals_.indirection_target); call->setArgument(2, locals_.indirection_cia); + GpVar target_ptr(c.newGpVar()); + call->setReturn(target_ptr); + + // Call target. + // void fn(ppc_state*, uint64_t) + call = c.call(target_ptr); + call->setComment("Indirection branch"); + call->setPrototype(kX86FuncConvDefault, + FuncBuilder2()); + call->setArgument(0, c.getGpArg(0)); + call->setArgument(1, locals_.indirection_cia); c.ret(); } @@ -936,16 +947,27 @@ int X64Emitter::GenerateIndirectionBranch(uint32_t cia, GpVar& target, // Spill registers. We could probably share this. SpillRegisters(); - // Issue the full indirection branch. + // Grab the target of the indirection. // TODO(benvanik): remove once fixed: https://code.google.com/p/asmjit/issues/detail?id=86 GpVar arg2 = c.newGpVar(kX86VarTypeGpq); c.mov(arg2, imm(cia)); X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch); call->setPrototype(kX86FuncConvDefault, - FuncBuilder3()); + FuncBuilder3()); call->setArgument(0, c.getGpArg(0)); call->setArgument(1, target); call->setArgument(2, arg2); + GpVar target_ptr(c.newGpVar()); + call->setReturn(target_ptr); + + // Call target. + // void fn(ppc_state*, uint64_t) + call = c.call(target_ptr); + call->setComment("Indirection branch"); + call->setPrototype(kX86FuncConvDefault, + FuncBuilder2()); + call->setArgument(0, c.getGpArg(0)); + call->setArgument(1, arg2); // TODO(benvanik): next_block/is_last_block/etc //if (next_block) { diff --git a/src/xenia/cpu/x64/x64_jit.cc b/src/xenia/cpu/x64/x64_jit.cc index 9f373a52f..61b036e56 100644 --- a/src/xenia/cpu/x64/x64_jit.cc +++ b/src/xenia/cpu/x64/x64_jit.cc @@ -138,23 +138,32 @@ int X64JIT::UninitModule(ExecModule* module) { return 0; } -int X64JIT::Execute(xe_ppc_state_t* ppc_state, FunctionSymbol* fn_symbol) { - XELOGCPU("Execute(%.8X): %s...", fn_symbol->start_address, fn_symbol->name()); - +void* X64JIT::GetFunctionPointer(sdb::FunctionSymbol* fn_symbol) { // Check function. x64_function_t fn_ptr = (x64_function_t)fn_symbol->impl_value; if (!fn_ptr) { // Function hasn't been prepped yet - make it now inline. // The emitter will lock and do other fancy things, if required. if (emitter_->PrepareFunction(fn_symbol)) { - XELOGCPU("Execute(%.8X): unable to make function %s", - fn_symbol->start_address, fn_symbol->name()); - return 1; + return NULL; } fn_ptr = (x64_function_t)fn_symbol->impl_value; XEASSERTNOTNULL(fn_ptr); } + return fn_ptr; +} + +int X64JIT::Execute(xe_ppc_state_t* ppc_state, FunctionSymbol* fn_symbol) { + XELOGCPU("Execute(%.8X): %s...", fn_symbol->start_address, fn_symbol->name()); + + x64_function_t fn_ptr = (x64_function_t)GetFunctionPointer(fn_symbol); + if (!fn_ptr) { + XELOGCPU("Execute(%.8X): unable to make function %s", + fn_symbol->start_address, fn_symbol->name()); + return 1; + } + // Call into the function. This will compile it if needed. fn_ptr(ppc_state, ppc_state->lr); diff --git a/src/xenia/cpu/x64/x64_jit.h b/src/xenia/cpu/x64/x64_jit.h index 4b6d4019d..16eb5e6b6 100644 --- a/src/xenia/cpu/x64/x64_jit.h +++ b/src/xenia/cpu/x64/x64_jit.h @@ -33,6 +33,7 @@ public: virtual int InitModule(ExecModule* module); virtual int UninitModule(ExecModule* module); + virtual void* GetFunctionPointer(sdb::FunctionSymbol* fn_symbol); virtual int Execute(xe_ppc_state_t* ppc_state, sdb::FunctionSymbol* fn_symbol);