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/global_exports.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/processor.h>
|
||||||
#include <xenia/cpu/sdb.h>
|
#include <xenia/cpu/sdb.h>
|
||||||
#include <xenia/cpu/ppc/instr.h>
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
#include <xenia/cpu/ppc/state.h>
|
#include <xenia/cpu/ppc/state.h>
|
||||||
|
@ -30,11 +31,12 @@ void _cdecl XeTrap(
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cdecl XeIndirectBranch(
|
void* _cdecl XeIndirectBranch(
|
||||||
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
||||||
XELOGCPU("INDIRECT BRANCH %.8X -> %.8X",
|
// TODO(benvanik): track this statistic - this path is very slow!
|
||||||
(uint32_t)br_ia, (uint32_t)target);
|
Processor* processor = (Processor*)state->processor;
|
||||||
XEASSERTALWAYS();
|
void* target_ptr = processor->GetFunctionPointer((uint32_t)target);
|
||||||
|
return target_ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void _cdecl XeInvalidInstruction(
|
void _cdecl XeInvalidInstruction(
|
||||||
|
|
|
@ -25,7 +25,7 @@ namespace cpu {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
void (_cdecl *XeTrap)(
|
void (_cdecl *XeTrap)(
|
||||||
xe_ppc_state_t* state, uint64_t cia);
|
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);
|
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia);
|
||||||
void (_cdecl *XeInvalidInstruction)(
|
void (_cdecl *XeInvalidInstruction)(
|
||||||
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
xe_ppc_state_t* state, uint64_t cia, uint64_t data);
|
||||||
|
|
|
@ -34,6 +34,7 @@ public:
|
||||||
virtual int InitModule(ExecModule* module) = 0;
|
virtual int InitModule(ExecModule* module) = 0;
|
||||||
virtual int UninitModule(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,
|
virtual int Execute(xe_ppc_state_t* ppc_state,
|
||||||
sdb::FunctionSymbol* fn_symbol) = 0;
|
sdb::FunctionSymbol* fn_symbol) = 0;
|
||||||
|
|
||||||
|
|
|
@ -185,27 +185,12 @@ void Processor::DeallocThread(ThreadState* thread_state) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||||
// Attempt to grab the function symbol from the global lookup table.
|
// Attempt to get the function.
|
||||||
FunctionSymbol* fn_symbol = sym_table_->GetFunction(address);
|
FunctionSymbol* fn_symbol = GetFunction(address);
|
||||||
if (!fn_symbol) {
|
if (!fn_symbol) {
|
||||||
// Search all modules for the function symbol.
|
// Symbol not found in any module.
|
||||||
// Each module will see if the address is within its code range and if the
|
XELOGCPU("Execute(%.8X): failed to find function", address);
|
||||||
// symbol is not found (likely) it will do analysis on it.
|
return 1;
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
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];
|
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);
|
void DeallocThread(ThreadState* thread_state);
|
||||||
int Execute(ThreadState* thread_state, uint32_t address);
|
int Execute(ThreadState* thread_state, uint32_t address);
|
||||||
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
||||||
|
sdb::FunctionSymbol* GetFunction(uint32_t address);
|
||||||
|
void* GetFunctionPointer(uint32_t address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
|
|
|
@ -448,18 +448,6 @@ int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||||
// TODO(benvanik): queue for add without expensive locks?
|
// TODO(benvanik): queue for add without expensive locks?
|
||||||
sym_table_->AddFunction(fn->start_address, fn);
|
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:
|
// For each basic block:
|
||||||
// - find outgoing target block or function
|
// - find outgoing target block or function
|
||||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn->blocks.begin();
|
||||||
|
@ -485,26 +473,19 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
// Function call.
|
// Function call.
|
||||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||||
if (block->outgoing_function) {
|
XEASSERTNOTNULL(block->outgoing_function);
|
||||||
FunctionSymbol::AddCall(fn, 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (new_fns.size()) {
|
XELOGSDB("Finished analyzing %.8X", fn->start_address);
|
||||||
XELOGW("Repeat analysis required to find %d new functions",
|
return 0;
|
||||||
(uint32_t)new_fns.size());
|
}
|
||||||
for (std::vector<uint32_t>::iterator it = new_fns.begin();
|
|
||||||
it != new_fns.end(); ++it) {
|
int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
GetOrInsertFunction(*it, fn);
|
// Find variable accesses.
|
||||||
}
|
// TODO(benvanik): data analysis to find variable accesses.
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,26 @@ using namespace xe::cpu::sdb;
|
||||||
|
|
||||||
|
|
||||||
SymbolTable::SymbolTable() {
|
SymbolTable::SymbolTable() {
|
||||||
|
lock_ = xe_mutex_alloc(10000);
|
||||||
|
XEASSERTNOTNULL(lock_);
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolTable::~SymbolTable() {
|
SymbolTable::~SymbolTable() {
|
||||||
|
xe_mutex_free(lock_);
|
||||||
|
lock_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
int SymbolTable::AddFunction(uint32_t address, FunctionSymbol* symbol) {
|
int SymbolTable::AddFunction(uint32_t address, FunctionSymbol* symbol) {
|
||||||
|
xe_mutex_lock(lock_);
|
||||||
map_[address] = symbol;
|
map_[address] = symbol;
|
||||||
|
xe_mutex_unlock(lock_);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionSymbol* SymbolTable::GetFunction(uint32_t address) {
|
FunctionSymbol* SymbolTable::GetFunction(uint32_t address) {
|
||||||
|
xe_mutex_lock(lock_);
|
||||||
FunctionMap::const_iterator it = map_.find(address);
|
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);
|
sdb::FunctionSymbol* GetFunction(uint32_t address);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// TODO(benvanik): replace with a better data structure.
|
||||||
|
xe_mutex_t* lock_;
|
||||||
typedef std::tr1::unordered_map<uint32_t, sdb::FunctionSymbol*> FunctionMap;
|
typedef std::tr1::unordered_map<uint32_t, sdb::FunctionSymbol*> FunctionMap;
|
||||||
FunctionMap map_;
|
FunctionMap map_;
|
||||||
};
|
};
|
||||||
|
|
|
@ -450,10 +450,21 @@ void X64Emitter::GenerateSharedBlocks() {
|
||||||
SpillRegisters();
|
SpillRegisters();
|
||||||
X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch);
|
X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch);
|
||||||
call->setPrototype(kX86FuncConvDefault,
|
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(0, c.getGpArg(0));
|
||||||
call->setArgument(1, locals_.indirection_target);
|
call->setArgument(1, locals_.indirection_target);
|
||||||
call->setArgument(2, locals_.indirection_cia);
|
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();
|
c.ret();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -936,16 +947,27 @@ int X64Emitter::GenerateIndirectionBranch(uint32_t cia, GpVar& target,
|
||||||
// Spill registers. We could probably share this.
|
// Spill registers. We could probably share this.
|
||||||
SpillRegisters();
|
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
|
// TODO(benvanik): remove once fixed: https://code.google.com/p/asmjit/issues/detail?id=86
|
||||||
GpVar arg2 = c.newGpVar(kX86VarTypeGpq);
|
GpVar arg2 = c.newGpVar(kX86VarTypeGpq);
|
||||||
c.mov(arg2, imm(cia));
|
c.mov(arg2, imm(cia));
|
||||||
X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch);
|
X86CompilerFuncCall* call = c.call(global_exports_.XeIndirectBranch);
|
||||||
call->setPrototype(kX86FuncConvDefault,
|
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(0, c.getGpArg(0));
|
||||||
call->setArgument(1, target);
|
call->setArgument(1, target);
|
||||||
call->setArgument(2, arg2);
|
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
|
// TODO(benvanik): next_block/is_last_block/etc
|
||||||
//if (next_block) {
|
//if (next_block) {
|
||||||
|
|
|
@ -138,23 +138,32 @@ int X64JIT::UninitModule(ExecModule* module) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int X64JIT::Execute(xe_ppc_state_t* ppc_state, FunctionSymbol* fn_symbol) {
|
void* X64JIT::GetFunctionPointer(sdb::FunctionSymbol* fn_symbol) {
|
||||||
XELOGCPU("Execute(%.8X): %s...", fn_symbol->start_address, fn_symbol->name());
|
|
||||||
|
|
||||||
// Check function.
|
// Check function.
|
||||||
x64_function_t fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
x64_function_t fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
||||||
if (!fn_ptr) {
|
if (!fn_ptr) {
|
||||||
// Function hasn't been prepped yet - make it now inline.
|
// Function hasn't been prepped yet - make it now inline.
|
||||||
// The emitter will lock and do other fancy things, if required.
|
// The emitter will lock and do other fancy things, if required.
|
||||||
if (emitter_->PrepareFunction(fn_symbol)) {
|
if (emitter_->PrepareFunction(fn_symbol)) {
|
||||||
XELOGCPU("Execute(%.8X): unable to make function %s",
|
return NULL;
|
||||||
fn_symbol->start_address, fn_symbol->name());
|
|
||||||
return 1;
|
|
||||||
}
|
}
|
||||||
fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
fn_ptr = (x64_function_t)fn_symbol->impl_value;
|
||||||
XEASSERTNOTNULL(fn_ptr);
|
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.
|
// Call into the function. This will compile it if needed.
|
||||||
fn_ptr(ppc_state, ppc_state->lr);
|
fn_ptr(ppc_state, ppc_state->lr);
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,7 @@ public:
|
||||||
virtual int InitModule(ExecModule* module);
|
virtual int InitModule(ExecModule* module);
|
||||||
virtual int UninitModule(ExecModule* module);
|
virtual int UninitModule(ExecModule* module);
|
||||||
|
|
||||||
|
virtual void* GetFunctionPointer(sdb::FunctionSymbol* fn_symbol);
|
||||||
virtual int Execute(xe_ppc_state_t* ppc_state,
|
virtual int Execute(xe_ppc_state_t* ppc_state,
|
||||||
sdb::FunctionSymbol* fn_symbol);
|
sdb::FunctionSymbol* fn_symbol);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue