Very basic, super slow, nasty indirection.

This commit is contained in:
Ben Vanik 2013-05-25 20:32:58 -07:00
parent 2986a0be82
commit 4073028188
11 changed files with 113 additions and 63 deletions

View File

@ -9,6 +9,7 @@
#include <xenia/cpu/global_exports.h>
#include <xenia/cpu/processor.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
@ -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(

View File

@ -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);

View File

@ -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;

View File

@ -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<ExecModule*>::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<ExecModule*>::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);
}

View File

@ -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_;

View File

@ -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<uint32_t> new_fns;
// For each basic block:
// - find outgoing target block or function
for (std::map<uint32_t, FunctionBlock*>::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<uint32_t>::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;
}

View File

@ -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;
}

View File

@ -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<uint32_t, sdb::FunctionSymbol*> FunctionMap;
FunctionMap map_;
};

View File

@ -450,10 +450,21 @@ void X64Emitter::GenerateSharedBlocks() {
SpillRegisters();
X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch);
call->setPrototype(kX86FuncConvDefault,
FuncBuilder3<void, void*, uint64_t, uint64_t>());
FuncBuilder3<void*, void*, uint64_t, uint64_t>());
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<void, void*, uint64_t>());
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<void, void*, uint64_t, uint64_t>());
FuncBuilder3<void*, void*, uint64_t, uint64_t>());
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<void, void*, uint64_t>());
call->setArgument(0, c.getGpArg(0));
call->setArgument(1, arg2);
// TODO(benvanik): next_block/is_last_block/etc
//if (next_block) {

View File

@ -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);

View File

@ -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);