Very basic, super slow, nasty indirection.
This commit is contained in:
parent
2986a0be82
commit
4073028188
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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_;
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue