Converting everything to C++ cause I'm a masochist.
This commit is contained in:
@@ -7,8 +7,9 @@
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "cpu/codegen.h"
|
||||
#include <xenia/cpu/codegen.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/DebugInfo.h>
|
||||
@@ -17,90 +18,90 @@
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
void xe_codegen_add_imports(xe_codegen_ctx_t *ctx);
|
||||
void xe_codegen_add_missing_import(
|
||||
xe_codegen_ctx_t *ctx, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export);
|
||||
void xe_codegen_add_import(
|
||||
xe_codegen_ctx_t *ctx, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export);
|
||||
void xe_codegen_add_function(xe_codegen_ctx_t *ctx, xe_sdb_function_t *fn);
|
||||
void xe_codegen_optimize(Module *m, Function *fn);
|
||||
CodegenContext::CodegenContext(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
UserModule* module, SymbolDatabase* sdb,
|
||||
LLVMContext* context, Module* gen_module) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_ = module;
|
||||
sdb_ = sdb;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
}
|
||||
|
||||
CodegenContext::~CodegenContext() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
llvm::Module *xe_codegen(xe_codegen_ctx_t *ctx,
|
||||
xe_codegen_options_t options) {
|
||||
LLVMContext& context = *ctx->context;
|
||||
int CodegenContext::GenerateModule() {
|
||||
std::string error_message;
|
||||
|
||||
// Initialize the module.
|
||||
Module *m = new Module(xe_module_get_name(ctx->module), context);
|
||||
ctx->gen_module = m;
|
||||
// TODO(benavnik): addModuleFlag?
|
||||
|
||||
// Link shared module into generated module.
|
||||
// This gives us a single module that we can optimize and prevents the need
|
||||
// for foreward declarations.
|
||||
Linker::LinkModules(m, ctx->shared_module, 0, &error_message);
|
||||
|
||||
// Setup a debug info builder.
|
||||
// This is used when creating any debug info. We may want to go more
|
||||
// fine grained than this, but for now it's something.
|
||||
xechar_t dir[2048];
|
||||
xestrcpy(dir, XECOUNT(dir), xe_module_get_path(ctx->module));
|
||||
XEIGNORE(xestrcpy(dir, XECOUNT(dir), module_->path()));
|
||||
xechar_t *slash = xestrrchr(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
ctx->di_builder = new DIBuilder(*m);
|
||||
ctx->di_builder->createCompileUnit(
|
||||
di_builder_ = new DIBuilder(*gen_module_);
|
||||
di_builder_->createCompileUnit(
|
||||
0,
|
||||
StringRef(xe_module_get_name(ctx->module)),
|
||||
StringRef(module_->name()),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
ctx->cu = (MDNode*)ctx->di_builder->getCU();
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
// Add import thunks/etc.
|
||||
xe_codegen_add_imports(ctx);
|
||||
AddImports();
|
||||
|
||||
// Add export wrappers.
|
||||
//
|
||||
|
||||
// Add all functions.
|
||||
xe_sdb_function_t **functions;
|
||||
size_t function_count;
|
||||
if (!xe_sdb_get_functions(ctx->sdb, &functions, &function_count)) {
|
||||
for (size_t n = 0; n < function_count; n++) {
|
||||
std::vector<FunctionSymbol*> functions;
|
||||
if (!sdb_->GetAllFunctions(functions)) {
|
||||
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
|
||||
it != functions.end(); ++it) {
|
||||
// kernel functions will be handled by the add imports handlers.
|
||||
if (functions[n]->type == kXESDBFunctionUser) {
|
||||
xe_codegen_add_function(ctx, functions[n]);
|
||||
if ((*it)->type == FunctionSymbol::User) {
|
||||
AddFunction(*it);
|
||||
}
|
||||
}
|
||||
xe_free(functions);
|
||||
}
|
||||
|
||||
ctx->di_builder->finalize();
|
||||
di_builder_->finalize();
|
||||
|
||||
return m;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_codegen_add_imports(xe_codegen_ctx_t *ctx) {
|
||||
xe_xex2_ref xex = xe_module_get_xex(ctx->module);
|
||||
const xe_xex2_header_t *header = xe_xex2_get_header(xex);
|
||||
void CodegenContext::AddImports() {
|
||||
xe_xex2_ref xex = module_->xex();
|
||||
const xe_xex2_header_t* header = xe_xex2_get_header(xex);
|
||||
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t *library = &header->import_libraries[n];
|
||||
const xe_xex2_import_library_t* library = &header->import_libraries[n];
|
||||
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
@@ -108,16 +109,15 @@ void xe_codegen_add_imports(xe_codegen_ctx_t *ctx) {
|
||||
&import_infos, &import_info_count));
|
||||
|
||||
for (size_t i = 0; i < import_info_count; i++) {
|
||||
const xe_xex2_import_info_t *info = &import_infos[i];
|
||||
xe_kernel_export_t *kernel_export =
|
||||
xe_kernel_export_resolver_get_by_ordinal(
|
||||
ctx->export_resolver, library->name, info->ordinal);
|
||||
if (!kernel_export || !xe_kernel_export_is_implemented(kernel_export)) {
|
||||
const xe_xex2_import_info_t* info = &import_infos[i];
|
||||
KernelExport* kernel_export = export_resolver_->GetExportByOrdinal(
|
||||
library->name, info->ordinal);
|
||||
if (!kernel_export || !kernel_export->IsImplemented()) {
|
||||
// Not implemented or known.
|
||||
xe_codegen_add_missing_import(ctx, library, info, kernel_export);
|
||||
AddMissingImport(library, info, kernel_export);
|
||||
} else {
|
||||
// Implemented.
|
||||
xe_codegen_add_import(ctx, library, info, kernel_export);
|
||||
AddPresentImport(library, info, kernel_export);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,10 +127,10 @@ void xe_codegen_add_imports(xe_codegen_ctx_t *ctx) {
|
||||
xe_xex2_release(xex);
|
||||
}
|
||||
|
||||
void xe_codegen_add_missing_import(
|
||||
xe_codegen_ctx_t *ctx, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export) {
|
||||
Module *m = ctx->gen_module;
|
||||
void CodegenContext::AddMissingImport(
|
||||
const xe_xex2_import_library_t* library,
|
||||
const xe_xex2_import_info_t* info, KernelExport* kernel_export) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
char name[128];
|
||||
@@ -149,11 +149,11 @@ void xe_codegen_add_missing_import(
|
||||
AttributeSet attrs = AttributeSet::get(context, awi);
|
||||
|
||||
std::vector<Type*> args;
|
||||
Type *return_type = Type::getInt32Ty(context);
|
||||
Type* return_type = Type::getInt32Ty(context);
|
||||
|
||||
FunctionType *ft = FunctionType::get(return_type,
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function *f = cast<Function>(m->getOrInsertFunction(
|
||||
Function* f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(name), ft, attrs));
|
||||
f->setCallingConv(CallingConv::C);
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
@@ -161,10 +161,10 @@ void xe_codegen_add_missing_import(
|
||||
// TODO(benvanik): log errors.
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> builder(block);
|
||||
Value *tmp = builder.getInt32(0);
|
||||
Value* tmp = builder.getInt32(0);
|
||||
builder.CreateRet(tmp);
|
||||
|
||||
xe_codegen_optimize(m, f);
|
||||
OptimizeFunction(m, f);
|
||||
|
||||
//GlobalAlias *alias = new GlobalAlias(f->getType(), GlobalValue::InternalLinkage, name, f, m);
|
||||
// printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
@@ -177,17 +177,17 @@ void xe_codegen_add_missing_import(
|
||||
}
|
||||
}
|
||||
|
||||
void xe_codegen_add_import(
|
||||
xe_codegen_ctx_t *ctx, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export) {
|
||||
// Module *m = ctx->gen_module;
|
||||
void CodegenContext::AddPresentImport(
|
||||
const xe_xex2_import_library_t* library,
|
||||
const xe_xex2_import_info_t* info, KernelExport* kernel_export) {
|
||||
// Module *m = gen_module_;
|
||||
// LLVMContext& context = m->getContext();
|
||||
|
||||
// TODO(benvanik): add import thunk code.
|
||||
}
|
||||
|
||||
void xe_codegen_add_function(xe_codegen_ctx_t *ctx, xe_sdb_function_t *fn) {
|
||||
Module *m = ctx->gen_module;
|
||||
void CodegenContext::AddFunction(FunctionSymbol* fn) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
AttributeWithIndex awi[] = {
|
||||
@@ -198,20 +198,20 @@ void xe_codegen_add_function(xe_codegen_ctx_t *ctx, xe_sdb_function_t *fn) {
|
||||
AttributeSet attrs = AttributeSet::get(context, awi);
|
||||
|
||||
std::vector<Type*> args;
|
||||
Type *return_type = Type::getInt32Ty(context);
|
||||
Type* return_type = Type::getInt32Ty(context);
|
||||
|
||||
char name[64];
|
||||
char *pname = name;
|
||||
char* pname = name;
|
||||
if (fn->name) {
|
||||
pname = fn->name;
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "fn_%.8X", fn->start_address);
|
||||
}
|
||||
|
||||
FunctionType *ft = FunctionType::get(return_type,
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function *f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(pname), ft, attrs));
|
||||
Function* f = cast<Function>(
|
||||
m->getOrInsertFunction(StringRef(pname), ft, attrs));
|
||||
f->setCallingConv(CallingConv::C);
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
@@ -219,21 +219,21 @@ void xe_codegen_add_function(xe_codegen_ctx_t *ctx, xe_sdb_function_t *fn) {
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> builder(block);
|
||||
//builder.SetCurrentDebugLocation(DebugLoc::get(fn->start_address >> 8, fn->start_address & 0xFF, ctx->cu));
|
||||
Value *tmp = builder.getInt32(0);
|
||||
Value* tmp = builder.getInt32(0);
|
||||
builder.CreateRet(tmp);
|
||||
|
||||
// i->setMetadata("some.name", MDNode::get(context, MDString::get(context, pname)));
|
||||
|
||||
uint8_t *mem = xe_memory_addr(ctx->memory, 0);
|
||||
uint32_t *pc = (uint32_t*)(mem + fn->start_address);
|
||||
uint8_t* mem = xe_memory_addr(memory_, 0);
|
||||
uint32_t* pc = (uint32_t*)(mem + fn->start_address);
|
||||
uint32_t pcdata = XEGETUINT32BE(pc);
|
||||
printf("data %.8X %.8X\n", fn->start_address, pcdata);
|
||||
xe_ppc_instr_type_t *instr_type = xe_ppc_get_instr_type(pcdata);
|
||||
InstrType* instr_type = ppc::GetInstrType(pcdata);
|
||||
if (instr_type) {
|
||||
printf("instr %.8X %s\n", fn->start_address, instr_type->name);
|
||||
xe_ppc_instr_t instr;
|
||||
instr.data.code = pcdata;
|
||||
printf("%d %d\n", instr.data.XFX.D, instr.data.XFX.spr);
|
||||
// xe_ppc_instr_t instr;
|
||||
// instr.data.code = pcdata;
|
||||
// printf("%d %d\n", instr.data.XFX.D, instr.data.XFX.spr);
|
||||
} else {
|
||||
printf("instr not found\n");
|
||||
}
|
||||
@@ -241,10 +241,10 @@ void xe_codegen_add_function(xe_codegen_ctx_t *ctx, xe_sdb_function_t *fn) {
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
xe_codegen_optimize(m, f);
|
||||
OptimizeFunction(m, f);
|
||||
}
|
||||
|
||||
void xe_codegen_optimize(Module *m, Function *fn) {
|
||||
void CodegenContext::OptimizeFunction(Module* m, Function* fn) {
|
||||
FunctionPassManager pm(m);
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_H_
|
||||
#define XENIA_CPU_CODEGEN_H_
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/kernel/module.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
int reserved;
|
||||
} xe_codegen_options_t;
|
||||
|
||||
typedef struct {
|
||||
xe_memory_ref memory;
|
||||
xe_kernel_export_resolver_ref export_resolver;
|
||||
xe_module_ref module;
|
||||
xe_sdb_ref sdb;
|
||||
|
||||
llvm::LLVMContext *context;
|
||||
llvm::Module *shared_module;
|
||||
llvm::Module *gen_module;
|
||||
llvm::DIBuilder *di_builder;
|
||||
llvm::MDNode *cu;
|
||||
} xe_codegen_ctx_t;
|
||||
|
||||
|
||||
llvm::Module *xe_codegen(xe_codegen_ctx_t *ctx,
|
||||
xe_codegen_options_t options);
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_H_
|
||||
306
src/cpu/cpu.cc
306
src/cpu/cpu.cc
@@ -1,306 +0,0 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu.h>
|
||||
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/Bitcode/ReaderWriter.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/ExecutionEngine/Interpreter.h>
|
||||
#include <llvm/ExecutionEngine/JIT.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/Support/ManagedStatic.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/system_error.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
#include "cpu/codegen.h"
|
||||
#include "cpu/xethunk/xethunk.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
||||
typedef struct {
|
||||
xe_module_ref module;
|
||||
xe_sdb_ref sdb;
|
||||
LLVMContext *context;
|
||||
Module *m;
|
||||
} xe_cpu_module_entry_t;
|
||||
|
||||
typedef struct xe_cpu {
|
||||
xe_ref_t ref;
|
||||
|
||||
xe_cpu_options_t options;
|
||||
|
||||
xe_pal_ref pal;
|
||||
xe_memory_ref memory;
|
||||
|
||||
std::vector<xe_cpu_module_entry_t> entries;
|
||||
|
||||
ExecutionEngine *engine;
|
||||
} xe_cpu_t;
|
||||
|
||||
|
||||
int xe_cpu_setup_engine(xe_cpu_ref cpu, Module *gen_module);
|
||||
int xe_cpu_init_module(xe_cpu_ref, Module *gen_module);
|
||||
void xe_cpu_uninit_module(xe_cpu_ref cpu, xe_cpu_module_entry_t *module_entry);
|
||||
|
||||
|
||||
xe_cpu_ref xe_cpu_create(xe_pal_ref pal, xe_memory_ref memory,
|
||||
xe_cpu_options_t options) {
|
||||
xe_cpu_ref cpu = (xe_cpu_ref)xe_calloc(sizeof(xe_cpu));
|
||||
xe_ref_init((xe_ref)cpu);
|
||||
|
||||
xe_copy_struct(&cpu->options, &options, sizeof(xe_cpu_options_t));
|
||||
|
||||
cpu->pal = xe_pal_retain(pal);
|
||||
cpu->memory = xe_memory_retain(memory);
|
||||
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
XEEXPECTTRUE(llvm_start_multithreaded());
|
||||
|
||||
return cpu;
|
||||
|
||||
XECLEANUP:
|
||||
xe_cpu_release(cpu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void xe_cpu_dealloc(xe_cpu_ref cpu) {
|
||||
// Cleanup all modules.
|
||||
for (std::vector<xe_cpu_module_entry_t>::iterator it = cpu->entries.begin();
|
||||
it != cpu->entries.end(); ++it) {
|
||||
xe_cpu_uninit_module(cpu, &*it);
|
||||
cpu->engine->removeModule(it->m);
|
||||
delete it->m;
|
||||
delete it->context;
|
||||
xe_sdb_release(it->sdb);
|
||||
xe_module_release(it->module);
|
||||
}
|
||||
|
||||
delete cpu->engine;
|
||||
llvm_shutdown();
|
||||
|
||||
xe_memory_release(cpu->memory);
|
||||
xe_pal_release(cpu->pal);
|
||||
}
|
||||
|
||||
xe_cpu_ref xe_cpu_retain(xe_cpu_ref cpu) {
|
||||
xe_ref_retain((xe_ref)cpu);
|
||||
return cpu;
|
||||
}
|
||||
|
||||
void xe_cpu_release(xe_cpu_ref cpu) {
|
||||
xe_ref_release((xe_ref)cpu, (xe_ref_dealloc_t)xe_cpu_dealloc);
|
||||
}
|
||||
|
||||
xe_pal_ref xe_cpu_get_pal(xe_cpu_ref cpu) {
|
||||
return xe_pal_retain(cpu->pal);
|
||||
}
|
||||
|
||||
xe_memory_ref xe_cpu_get_memory(xe_cpu_ref cpu) {
|
||||
return xe_memory_retain(cpu->memory);
|
||||
}
|
||||
|
||||
int xe_cpu_setup_engine(xe_cpu_ref cpu, Module *gen_module) {
|
||||
if (cpu->engine) {
|
||||
// Engine already initialized - just add the module.
|
||||
cpu->engine->addModule(gen_module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string error_message;
|
||||
cpu->engine = ExecutionEngine::create(gen_module, false, &error_message,
|
||||
CodeGenOpt::Aggressive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module,
|
||||
xe_kernel_export_resolver_ref export_resolver) {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
xe_sdb_ref sdb = NULL;
|
||||
LLVMContext *context = NULL;
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
Module *gen_module = NULL;
|
||||
Module *shared_module = NULL;
|
||||
raw_ostream *outs = NULL;
|
||||
|
||||
PassManager pm;
|
||||
PassManagerBuilder pmb;
|
||||
|
||||
// TODO(benvanik): embed the bc file into the emulator.
|
||||
const char *thunk_path = "src/cpu/xethunk/xethunk.bc";
|
||||
|
||||
// Create a LLVM context for this prepare.
|
||||
// This is required to ensure thread safety/etc.
|
||||
context = new LLVMContext();
|
||||
|
||||
// Calculate a cache path based on the module, the CPU version, and other
|
||||
// bits.
|
||||
// TODO(benvanik): cache path calculation.
|
||||
const char *cache_path = "build/generated.bc";
|
||||
|
||||
// Check the cache to see if the bitcode exists.
|
||||
// If it does, load that module directly. In the future we could also cache
|
||||
// on linked binaries but that requires more safety around versioning.
|
||||
// TODO(benvanik): check cache for module bitcode and load.
|
||||
// if (path_exists(cache_key)) {
|
||||
// gen_module = load_bitcode(cache_key);
|
||||
// sdb = load_symbol_table(cache_key);
|
||||
// }
|
||||
|
||||
// If not found in cache, generate a new module.
|
||||
if (!gen_module) {
|
||||
// Load shared bitcode files.
|
||||
// These contain globals and common thunk code that are used by the
|
||||
// generated code.
|
||||
XEEXPECTZERO(MemoryBuffer::getFile(thunk_path, shared_module_buffer));
|
||||
shared_module = ParseBitcodeFile(&*shared_module_buffer, *context,
|
||||
&error_message);
|
||||
XEEXPECTNOTNULL(shared_module);
|
||||
|
||||
// Analyze the module and add its symbols to the symbol database.
|
||||
sdb = xe_sdb_create(cpu->memory, module);
|
||||
XEEXPECTNOTNULL(sdb);
|
||||
xe_sdb_dump(sdb);
|
||||
|
||||
// Build the module from the source code.
|
||||
xe_codegen_options_t codegen_options;
|
||||
xe_zero_struct(&codegen_options, sizeof(codegen_options));
|
||||
xe_codegen_ctx_t codegen_ctx;
|
||||
xe_zero_struct(&codegen_ctx, sizeof(codegen_ctx));
|
||||
codegen_ctx.memory = cpu->memory;
|
||||
codegen_ctx.export_resolver = export_resolver;
|
||||
codegen_ctx.module = module;
|
||||
codegen_ctx.sdb = sdb;
|
||||
codegen_ctx.context = context;
|
||||
codegen_ctx.shared_module = shared_module;
|
||||
gen_module = xe_codegen(&codegen_ctx, codegen_options);
|
||||
|
||||
// Write to cache.
|
||||
outs = new raw_fd_ostream(cache_path, error_message,
|
||||
raw_fd_ostream::F_Binary);
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module, *outs);
|
||||
}
|
||||
|
||||
// Link optimizations.
|
||||
XEEXPECTZERO(gen_module->MaterializeAllPermanently(&error_message));
|
||||
|
||||
// Reset target triple (ignore what's in xethunk).
|
||||
gen_module->setTargetTriple(llvm::sys::getDefaultTargetTriple());
|
||||
|
||||
// Run full module optimizations.
|
||||
pm.add(new DataLayout(gen_module));
|
||||
pm.add(createVerifierPass());
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateModulePassManager(pm);
|
||||
pmb.populateLTOPassManager(pm, false, true);
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*gen_module);
|
||||
|
||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
||||
|
||||
// Setup the execution engine (if needed).
|
||||
// The engine is required to get the data layout and other values.
|
||||
XEEXPECTZERO(xe_cpu_setup_engine(cpu, gen_module));
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(xe_cpu_init_module(cpu, gen_module));
|
||||
|
||||
// Force JIT of all functions.
|
||||
for (Module::iterator I = gen_module->begin(), E = gen_module->end();
|
||||
I != E; I++) {
|
||||
Function* fn = &*I;
|
||||
if (!fn->isDeclaration()) {
|
||||
cpu->engine->getPointerToFunction(fn);
|
||||
}
|
||||
}
|
||||
|
||||
gen_module->dump();
|
||||
|
||||
// Stash the module entry to allow cleanup later.
|
||||
xe_cpu_module_entry_t module_entry;
|
||||
module_entry.module = xe_module_retain(module);
|
||||
module_entry.sdb = xe_sdb_retain(sdb);
|
||||
module_entry.context = context;
|
||||
module_entry.m = gen_module;
|
||||
cpu->entries.push_back(module_entry);
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
delete outs;
|
||||
delete shared_module;
|
||||
if (result_code) {
|
||||
delete gen_module;
|
||||
delete context;
|
||||
}
|
||||
xe_sdb_release(sdb);
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int xe_cpu_init_module(xe_cpu_ref cpu, Module *gen_module) {
|
||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||
cpu->engine->runStaticConstructorsDestructors(gen_module, false);
|
||||
|
||||
// Prepare init options.
|
||||
xe_module_init_options_t init_options;
|
||||
xe_zero_struct(&init_options, sizeof(init_options));
|
||||
init_options.memory_base = xe_memory_addr(cpu->memory, 0);
|
||||
|
||||
// Grab the init function and call it.
|
||||
Function *xe_module_init = gen_module->getFunction("xe_module_init");
|
||||
std::vector<GenericValue> args;
|
||||
args.push_back(GenericValue(&init_options));
|
||||
GenericValue ret = cpu->engine->runFunction(xe_module_init, args);
|
||||
|
||||
return ret.IntVal.getSExtValue();
|
||||
}
|
||||
|
||||
void xe_cpu_uninit_module(xe_cpu_ref cpu, xe_cpu_module_entry_t *module_entry) {
|
||||
// Grab function and call it.
|
||||
Function *xe_module_uninit = module_entry->m->getFunction("xe_module_uninit");
|
||||
std::vector<GenericValue> args;
|
||||
cpu->engine->runFunction(xe_module_uninit, args);
|
||||
|
||||
// Run static destructors.
|
||||
cpu->engine->runStaticConstructorsDestructors(module_entry->m, true);
|
||||
}
|
||||
|
||||
int xe_cpu_execute(xe_cpu_ref cpu, uint32_t address) {
|
||||
// TODO(benvanik): implement execute.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t xe_cpu_create_callback(xe_cpu_ref cpu,
|
||||
void (*callback)(void*), void *data) {
|
||||
// TODO(benvanik): implement callback creation.
|
||||
return 0;
|
||||
}
|
||||
201
src/cpu/exec_module.cc
Normal file
201
src/cpu/exec_module.cc
Normal file
@@ -0,0 +1,201 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/Bitcode/ReaderWriter.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/Host.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/system_error.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/codegen.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
#include "cpu/xethunk/xethunk.h"
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ExecModule::ExecModule(
|
||||
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
||||
UserModule* user_module, shared_ptr<llvm::ExecutionEngine>& engine) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_ = user_module;
|
||||
engine_ = engine;
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::SymbolDatabase(memory_, module_));
|
||||
|
||||
context_ = shared_ptr<LLVMContext>(new LLVMContext());
|
||||
}
|
||||
|
||||
ExecModule::~ExecModule() {
|
||||
if (gen_module_) {
|
||||
Uninit();
|
||||
engine_->removeModule(gen_module_.get());
|
||||
}
|
||||
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ExecModule::Prepare() {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
auto_ptr<Module> shared_module;
|
||||
auto_ptr<raw_ostream> outs;
|
||||
auto_ptr<CodegenContext> codegen;
|
||||
|
||||
PassManager pm;
|
||||
PassManagerBuilder pmb;
|
||||
|
||||
// TODO(benvanik): embed the bc file into the emulator.
|
||||
const char *thunk_path = "src/cpu/xethunk/xethunk.bc";
|
||||
|
||||
// Calculate a cache path based on the module, the CPU version, and other
|
||||
// bits.
|
||||
// TODO(benvanik): cache path calculation.
|
||||
const char *cache_path = "build/generated.bc";
|
||||
|
||||
// Check the cache to see if the bitcode exists.
|
||||
// If it does, load that module directly. In the future we could also cache
|
||||
// on linked binaries but that requires more safety around versioning.
|
||||
// TODO(benvanik): check cache for module bitcode and load.
|
||||
// if (path_exists(cache_key)) {
|
||||
// exec_module = load_bitcode(cache_key);
|
||||
// sdb = load_symbol_table(cache_key);
|
||||
// }
|
||||
|
||||
// If not found in cache, generate a new module.
|
||||
if (!gen_module_.get()) {
|
||||
// Load shared bitcode files.
|
||||
// These contain globals and common thunk code that are used by the
|
||||
// generated code.
|
||||
XEEXPECTZERO(MemoryBuffer::getFile(thunk_path, shared_module_buffer));
|
||||
shared_module = auto_ptr<Module>(ParseBitcodeFile(
|
||||
&*shared_module_buffer, *context_, &error_message));
|
||||
XEEXPECTNOTNULL(shared_module.get());
|
||||
|
||||
// Analyze the module and add its symbols to the symbol database.
|
||||
XEEXPECTZERO(sdb_->Analyze());
|
||||
|
||||
// Initialize the module.
|
||||
gen_module_ = shared_ptr<Module>(
|
||||
new Module(module_->name(), *context_.get()));
|
||||
// TODO(benavnik): addModuleFlag?
|
||||
|
||||
// Link shared module into generated module.
|
||||
// This gives us a single module that we can optimize and prevents the need
|
||||
// for foreward declarations.
|
||||
Linker::LinkModules(gen_module_.get(), shared_module.get(), 0,
|
||||
&error_message);
|
||||
|
||||
// Build the module from the source code.
|
||||
codegen = auto_ptr<CodegenContext>(new CodegenContext(
|
||||
memory_, export_resolver_.get(), module_, sdb_.get(),
|
||||
context_.get(), gen_module_.get()));
|
||||
XEEXPECTZERO(codegen->GenerateModule());
|
||||
|
||||
// Write to cache.
|
||||
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
|
||||
cache_path, error_message, raw_fd_ostream::F_Binary));
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module_.get(), *outs);
|
||||
}
|
||||
|
||||
// Link optimizations.
|
||||
XEEXPECTZERO(gen_module_->MaterializeAllPermanently(&error_message));
|
||||
|
||||
// Reset target triple (ignore what's in xethunk).
|
||||
gen_module_->setTargetTriple(llvm::sys::getDefaultTargetTriple());
|
||||
|
||||
// Run full module optimizations.
|
||||
pm.add(new DataLayout(gen_module_.get()));
|
||||
pm.add(createVerifierPass());
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateModulePassManager(pm);
|
||||
pmb.populateLTOPassManager(pm, false, true);
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*gen_module_);
|
||||
|
||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(Init());
|
||||
|
||||
// Force JIT of all functions.
|
||||
for (Module::iterator I = gen_module_->begin(), E = gen_module_->end();
|
||||
I != E; I++) {
|
||||
Function* fn = &*I;
|
||||
if (!fn->isDeclaration()) {
|
||||
engine_->getPointerToFunction(fn);
|
||||
}
|
||||
}
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int ExecModule::Init() {
|
||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
|
||||
|
||||
// Prepare init options.
|
||||
xe_module_init_options_t init_options;
|
||||
xe_zero_struct(&init_options, sizeof(init_options));
|
||||
init_options.memory_base = xe_memory_addr(memory_, 0);
|
||||
|
||||
// Grab the init function and call it.
|
||||
Function* xe_module_init = gen_module_->getFunction("xe_module_init");
|
||||
std::vector<GenericValue> args;
|
||||
args.push_back(GenericValue(&init_options));
|
||||
GenericValue ret = engine_->runFunction(xe_module_init, args);
|
||||
|
||||
return ret.IntVal.getSExtValue();
|
||||
}
|
||||
|
||||
int ExecModule::Uninit() {
|
||||
// Grab function and call it.
|
||||
Function* xe_module_uninit = gen_module_->getFunction("xe_module_uninit");
|
||||
std::vector<GenericValue> args;
|
||||
engine_->runFunction(xe_module_uninit, args);
|
||||
|
||||
// Run static destructors.
|
||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ExecModule::Dump() {
|
||||
gen_module_->dump();
|
||||
}
|
||||
@@ -9,46 +9,49 @@
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
#include "cpu/ppc/instr_table.h"
|
||||
#include "cpu/ppc/instr_tables.h"
|
||||
|
||||
|
||||
xe_ppc_instr_type_t *xe_ppc_get_instr_type(uint32_t code) {
|
||||
xe_ppc_instr_type_t *slot = NULL;
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
InstrType* xe::cpu::ppc::GetInstrType(uint32_t code) {
|
||||
InstrType* slot = NULL;
|
||||
switch (code >> 26) {
|
||||
case 4:
|
||||
// Opcode = 4, index = bits 5-0 (6)
|
||||
slot = &xe_ppc_instr_table_4[XESELECTBITS(code, 0, 5)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_4[XESELECTBITS(code, 0, 5)];
|
||||
break;
|
||||
case 19:
|
||||
// Opcode = 19, index = bits 10-1 (10)
|
||||
slot = &xe_ppc_instr_table_19[XESELECTBITS(code, 1, 10)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_19[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
case 30:
|
||||
// Opcode = 30, index = bits 5-1 (5)
|
||||
slot = &xe_ppc_instr_table_30[XESELECTBITS(code, 1, 5)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_30[XESELECTBITS(code, 1, 5)];
|
||||
break;
|
||||
case 31:
|
||||
// Opcode = 31, index = bits 10-1 (10)
|
||||
slot = &xe_ppc_instr_table_31[XESELECTBITS(code, 1, 10)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_31[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
case 58:
|
||||
// Opcode = 58, index = bits 1-0 (2)
|
||||
slot = &xe_ppc_instr_table_58[XESELECTBITS(code, 0, 1)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_58[XESELECTBITS(code, 0, 1)];
|
||||
break;
|
||||
case 59:
|
||||
// Opcode = 59, index = bits 5-1 (5)
|
||||
slot = &xe_ppc_instr_table_59[XESELECTBITS(code, 1, 5)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_59[XESELECTBITS(code, 1, 5)];
|
||||
break;
|
||||
case 62:
|
||||
// Opcode = 62, index = bits 1-0 (2)
|
||||
slot = &xe_ppc_instr_table_62[XESELECTBITS(code, 0, 1)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_62[XESELECTBITS(code, 0, 1)];
|
||||
break;
|
||||
case 63:
|
||||
// Opcode = 63, index = bits 10-1 (10)
|
||||
slot = &xe_ppc_instr_table_63[XESELECTBITS(code, 1, 10)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table_63[XESELECTBITS(code, 1, 10)];
|
||||
break;
|
||||
default:
|
||||
slot = &xe_ppc_instr_table[XESELECTBITS(code, 26, 31)];
|
||||
slot = &xe::cpu::ppc::tables::instr_table[XESELECTBITS(code, 26, 31)];
|
||||
break;
|
||||
}
|
||||
if (!slot || !slot->opcode) {
|
||||
@@ -57,8 +60,8 @@ xe_ppc_instr_type_t *xe_ppc_get_instr_type(uint32_t code) {
|
||||
return slot;
|
||||
}
|
||||
|
||||
int xe_ppc_register_instr_emit(uint32_t code, xe_ppc_emit_fn emit) {
|
||||
xe_ppc_instr_type_t *instr_type = xe_ppc_get_instr_type(code);
|
||||
int xe::cpu::ppc::RegisterInstrEmit(uint32_t code, InstrEmitFn emit) {
|
||||
InstrType* instr_type = GetInstrType(code);
|
||||
if (!instr_type) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -13,28 +13,47 @@
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
llvm::LLVMContext *context;
|
||||
llvm::Module *module;
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
|
||||
|
||||
class InstrContext {
|
||||
using namespace llvm;
|
||||
public:
|
||||
InstrContext();
|
||||
|
||||
Value* cia();
|
||||
Value* nia();
|
||||
void set_nia(Value* value);
|
||||
Value* spr(uint32_t n);
|
||||
void set_spr(uint32_t n, Value* value);
|
||||
|
||||
Value* cr();
|
||||
void set_cr(Value* value);
|
||||
|
||||
Value* gpr(uint32_t n);
|
||||
void set_gpr(uint32_t n, Value* value);
|
||||
|
||||
Value* get_memory_addr(uint32_t addr);
|
||||
Value* read_memory(Value* addr, uint32_t size, bool extend);
|
||||
void write_memory(Value* addr, uint32_t size, Value* value);
|
||||
|
||||
LLVMContext& context;
|
||||
Module& module;
|
||||
// TODO(benvanik): IRBuilder/etc
|
||||
|
||||
// Address of the instruction being generated.
|
||||
uint32_t cia;
|
||||
uint32_t cia;
|
||||
|
||||
llvm::Value *get_cia();
|
||||
void set_nia(llvm::Value *value);
|
||||
llvm::Value *get_spr(uint32_t n);
|
||||
void set_spr(uint32_t n, llvm::Value *value);
|
||||
private:
|
||||
//
|
||||
};
|
||||
|
||||
llvm::Value *get_cr();
|
||||
void set_cr(llvm::Value *value);
|
||||
|
||||
llvm::Value *get_gpr(uint32_t n);
|
||||
void set_gpr(uint32_t n, llvm::Value *value);
|
||||
|
||||
llvm::Value *get_memory_addr(uint32_t addr);
|
||||
llvm::Value *read_memory(llvm::Value *addr, uint32_t size, bool extend);
|
||||
void write_memory(llvm::Value *addr, uint32_t size, llvm::Value *value);
|
||||
} xe_ppc_instr_ctx_t;
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_INSTR_CTX_H_
|
||||
|
||||
@@ -13,11 +13,16 @@
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_type_t *unprep, int unprep_count, int a, int b) {
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace ppc {
|
||||
namespace tables {
|
||||
|
||||
|
||||
static InstrType* instr_table_prep(
|
||||
InstrType* unprep, int unprep_count, int a, int b) {
|
||||
int prep_count = pow(2, b - a + 1);
|
||||
xe_ppc_instr_type_t *prep = (xe_ppc_instr_type_t*)xe_calloc(
|
||||
prep_count * sizeof(xe_ppc_instr_type_t));
|
||||
InstrType* prep = (InstrType*)xe_calloc(prep_count * sizeof(InstrType));
|
||||
for (int n = 0; n < unprep_count; n++) {
|
||||
int ordinal = XESELECTBITS(unprep[n].opcode, a, b);
|
||||
prep[ordinal] = unprep[n];
|
||||
@@ -42,15 +47,15 @@ static xe_ppc_instr_type_t *xe_ppc_instr_table_prep(
|
||||
// PowerISA_V2.06B_V2_PUBLIC.pdf
|
||||
|
||||
// Opcode = 4, index = bits 5-0 (6)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_4_unprep[] = {
|
||||
static InstrType instr_table_4_unprep[] = {
|
||||
// TODO: all of the vector ops
|
||||
INSTRUCTION(vperm, 0x1000002B, VA , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_4 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_4_unprep, XECOUNT(xe_ppc_instr_table_4_unprep), 0, 5);
|
||||
static InstrType* instr_table_4 = instr_table_prep(
|
||||
instr_table_4_unprep, XECOUNT(instr_table_4_unprep), 0, 5);
|
||||
|
||||
// Opcode = 19, index = bits 10-1 (10)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_19_unprep[] = {
|
||||
static InstrType instr_table_19_unprep[] = {
|
||||
INSTRUCTION(mcrf, 0x4C000000, XL , General , 0),
|
||||
INSTRUCTION(bclrx, 0x4C000020, XL , BranchCond , 0),
|
||||
INSTRUCTION(crnor, 0x4C000042, XL , General , 0),
|
||||
@@ -64,11 +69,11 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_19_unprep[] = {
|
||||
INSTRUCTION(cror, 0x4C000382, XL , General , 0),
|
||||
INSTRUCTION(bcctrx, 0x4C000420, XL , BranchCond , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_19 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_19_unprep, XECOUNT(xe_ppc_instr_table_19_unprep), 1, 10);
|
||||
static InstrType* instr_table_19 = instr_table_prep(
|
||||
instr_table_19_unprep, XECOUNT(instr_table_19_unprep), 1, 10);
|
||||
|
||||
// Opcode = 30, index = bits 5-1 (5)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_30_unprep[] = {
|
||||
static InstrType instr_table_30_unprep[] = {
|
||||
INSTRUCTION(rldiclx, 0x78000000, MD , General , 0),
|
||||
INSTRUCTION(rldicrx, 0x78000004, MD , General , 0),
|
||||
INSTRUCTION(rldicx, 0x78000008, MD , General , 0),
|
||||
@@ -76,11 +81,11 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_30_unprep[] = {
|
||||
INSTRUCTION(rldclx, 0x78000010, MDS, General , 0),
|
||||
INSTRUCTION(rldcrx, 0x78000012, MDS, General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_30 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_30_unprep, XECOUNT(xe_ppc_instr_table_30_unprep), 1, 5);
|
||||
static InstrType* instr_table_30 = instr_table_prep(
|
||||
instr_table_30_unprep, XECOUNT(instr_table_30_unprep), 1, 5);
|
||||
|
||||
// Opcode = 31, index = bits 10-1 (10)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_31_unprep[] = {
|
||||
static InstrType instr_table_31_unprep[] = {
|
||||
INSTRUCTION(cmp, 0x7C000000, X , General , 0),
|
||||
INSTRUCTION(tw, 0x7C000008, X , General , 0),
|
||||
INSTRUCTION(lvsl, 0x7C00000C, X , General , 0),
|
||||
@@ -199,20 +204,20 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_31_unprep[] = {
|
||||
INSTRUCTION(extswx, 0x7C0007B4, X , General , 0),
|
||||
INSTRUCTION(dcbz, 0x7C0007EC, X , General , 0), // 0x7C2007EC = DCBZ128
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_31 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_31_unprep, XECOUNT(xe_ppc_instr_table_31_unprep), 1, 10);
|
||||
static InstrType* instr_table_31 = instr_table_prep(
|
||||
instr_table_31_unprep, XECOUNT(instr_table_31_unprep), 1, 10);
|
||||
|
||||
// Opcode = 58, index = bits 1-0 (2)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_58_unprep[] = {
|
||||
static InstrType instr_table_58_unprep[] = {
|
||||
INSTRUCTION(ld, 0xE8000000, DS , General , 0),
|
||||
INSTRUCTION(ldu, 0xE8000001, DS , General , 0),
|
||||
INSTRUCTION(lwa, 0xE8000002, DS , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_58 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_58_unprep, XECOUNT(xe_ppc_instr_table_58_unprep), 0, 1);
|
||||
static InstrType* instr_table_58 = instr_table_prep(
|
||||
instr_table_58_unprep, XECOUNT(instr_table_58_unprep), 0, 1);
|
||||
|
||||
// Opcode = 59, index = bits 5-1 (5)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_59_unprep[] = {
|
||||
static InstrType instr_table_59_unprep[] = {
|
||||
INSTRUCTION(fdivsx, 0xEC000024, A , General , 0),
|
||||
INSTRUCTION(fsubsx, 0xEC000028, A , General , 0),
|
||||
INSTRUCTION(faddsx, 0xEC00002A, A , General , 0),
|
||||
@@ -224,19 +229,19 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_59_unprep[] = {
|
||||
INSTRUCTION(fnmsubsx, 0xEC00003C, A , General , 0),
|
||||
INSTRUCTION(fnmaddsx, 0xEC00003E, A , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_59 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_59_unprep, XECOUNT(xe_ppc_instr_table_59_unprep), 1, 5);
|
||||
static InstrType* instr_table_59 = instr_table_prep(
|
||||
instr_table_59_unprep, XECOUNT(instr_table_59_unprep), 1, 5);
|
||||
|
||||
// Opcode = 62, index = bits 1-0 (2)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_62_unprep[] = {
|
||||
static InstrType instr_table_62_unprep[] = {
|
||||
INSTRUCTION(std, 0xF8000000, DS , General , 0),
|
||||
INSTRUCTION(stdu, 0xF8000001, DS , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_62 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_62_unprep, XECOUNT(xe_ppc_instr_table_62_unprep), 0, 1);
|
||||
static InstrType* instr_table_62 = instr_table_prep(
|
||||
instr_table_62_unprep, XECOUNT(instr_table_62_unprep), 0, 1);
|
||||
|
||||
// Opcode = 63, index = bits 10-1 (10)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_63_unprep[] = {
|
||||
static InstrType instr_table_63_unprep[] = {
|
||||
INSTRUCTION(fcmpu, 0xFC000000, X , General , 0),
|
||||
INSTRUCTION(frspx, 0xFC000018, X , General , 0),
|
||||
INSTRUCTION(fctiwx, 0xFC00001C, X , General , 0),
|
||||
@@ -267,11 +272,11 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_63_unprep[] = {
|
||||
INSTRUCTION(fctidzx, 0xFC00065E, X , General , 0),
|
||||
INSTRUCTION(fcfidx, 0xFC00069C, X , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table_63 = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_63_unprep, XECOUNT(xe_ppc_instr_table_63_unprep), 1, 10);
|
||||
static InstrType* instr_table_63 = instr_table_prep(
|
||||
instr_table_63_unprep, XECOUNT(instr_table_63_unprep), 1, 10);
|
||||
|
||||
// Main table, index = bits 31-26 (6) : (code >> 26)
|
||||
static xe_ppc_instr_type_t xe_ppc_instr_table_unprep[64] = {
|
||||
static InstrType instr_table_unprep[64] = {
|
||||
INSTRUCTION(tdi, 0x08000000, D , General , 0),
|
||||
INSTRUCTION(twi, 0x0C000000, D , General , 0),
|
||||
INSTRUCTION(mulli, 0x1C000000, D , General , 0),
|
||||
@@ -319,12 +324,19 @@ static xe_ppc_instr_type_t xe_ppc_instr_table_unprep[64] = {
|
||||
INSTRUCTION(stfd, 0xD8000000, D , General , 0),
|
||||
INSTRUCTION(stfdu, 0xDC000000, D , General , 0),
|
||||
};
|
||||
static xe_ppc_instr_type_t *xe_ppc_instr_table = xe_ppc_instr_table_prep(
|
||||
xe_ppc_instr_table_unprep, XECOUNT(xe_ppc_instr_table_unprep), 26, 31);
|
||||
static InstrType* instr_table = instr_table_prep(
|
||||
instr_table_unprep, XECOUNT(instr_table_unprep), 26, 31);
|
||||
|
||||
|
||||
#undef FLAG
|
||||
#undef INSTRUCTION
|
||||
#undef EMPTY
|
||||
|
||||
|
||||
} // namespace tables
|
||||
} // namespace ppc
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_PPC_INSTR_TABLE_H_
|
||||
105
src/cpu/processor.cc
Normal file
105
src/cpu/processor.cc
Normal file
@@ -0,0 +1,105 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/ExecutionEngine/Interpreter.h>
|
||||
#include <llvm/ExecutionEngine/JIT.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/ManagedStatic.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
|
||||
pal_ = xe_pal_retain(pal);
|
||||
memory_ = xe_memory_retain(memory);
|
||||
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
}
|
||||
|
||||
Processor::~Processor() {
|
||||
// Cleanup all modules.
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
delete *it;
|
||||
}
|
||||
|
||||
engine_.reset();
|
||||
llvm_shutdown();
|
||||
|
||||
xe_memory_release(memory_);
|
||||
xe_pal_release(pal_);
|
||||
}
|
||||
|
||||
xe_pal_ref Processor::pal() {
|
||||
return xe_pal_retain(pal_);
|
||||
}
|
||||
|
||||
xe_memory_ref Processor::memory() {
|
||||
return xe_memory_retain(memory_);
|
||||
}
|
||||
|
||||
int Processor::Setup() {
|
||||
XEASSERTNULL(engine_);
|
||||
|
||||
if (!llvm_start_multithreaded()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
LLVMContext* dummy_context = new LLVMContext();
|
||||
Module* dummy_module = new Module("dummy", *dummy_context);
|
||||
|
||||
std::string error_message;
|
||||
engine_ = shared_ptr<ExecutionEngine>(
|
||||
ExecutionEngine::create(dummy_module, false, &error_message,
|
||||
CodeGenOpt::Aggressive));
|
||||
if (!engine_) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Processor::PrepareModule(UserModule* user_module,
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
ExecModule* exec_module = new ExecModule(
|
||||
memory_, export_resolver, user_module, engine_);
|
||||
|
||||
if (exec_module->Prepare()) {
|
||||
delete exec_module;
|
||||
return 1;
|
||||
}
|
||||
|
||||
modules_.push_back(exec_module);
|
||||
|
||||
user_module->Dump(export_resolver.get());
|
||||
exec_module->Dump();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Processor::Execute(uint32_t address) {
|
||||
// TODO(benvanik): implement execute.
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
|
||||
// TODO(benvanik): implement callback creation.
|
||||
return 0;
|
||||
}
|
||||
297
src/cpu/sdb.cc
297
src/cpu/sdb.cc
@@ -13,151 +13,124 @@
|
||||
#include <map>
|
||||
|
||||
|
||||
typedef std::map<uint32_t, xe_sdb_symbol_t> xe_sdb_symbol_map;
|
||||
typedef std::list<xe_sdb_function_t*> xe_sdb_function_queue;
|
||||
|
||||
struct xe_sdb {
|
||||
xe_ref_t ref;
|
||||
|
||||
xe_memory_ref memory;
|
||||
|
||||
size_t function_count;
|
||||
size_t variable_count;
|
||||
xe_sdb_symbol_map *symbols;
|
||||
xe_sdb_function_queue *scan_queue;
|
||||
};
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
int xe_sdb_analyze_module(xe_sdb_ref sdb, xe_module_ref module);
|
||||
|
||||
|
||||
xe_sdb_ref xe_sdb_create(xe_memory_ref memory, xe_module_ref module) {
|
||||
xe_sdb_ref sdb = (xe_sdb_ref)xe_calloc(sizeof(xe_sdb));
|
||||
xe_ref_init((xe_ref)sdb);
|
||||
|
||||
sdb->memory = xe_memory_retain(memory);
|
||||
|
||||
sdb->symbols = new xe_sdb_symbol_map();
|
||||
sdb->scan_queue = new xe_sdb_function_queue();
|
||||
|
||||
XEEXPECTZERO(xe_sdb_analyze_module(sdb, module));
|
||||
|
||||
return sdb;
|
||||
|
||||
XECLEANUP:
|
||||
xe_sdb_release(sdb);
|
||||
return NULL;
|
||||
SymbolDatabase::SymbolDatabase(xe_memory_ref memory, UserModule* module) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
module_ = module;
|
||||
}
|
||||
|
||||
void xe_sdb_dealloc(xe_sdb_ref sdb) {
|
||||
// TODO(benvanik): release strdup results
|
||||
|
||||
for (xe_sdb_symbol_map::iterator it = sdb->symbols->begin(); it !=
|
||||
sdb->symbols->end(); ++it) {
|
||||
switch (it->second.type) {
|
||||
case 0:
|
||||
delete it->second.function;
|
||||
break;
|
||||
case 1:
|
||||
delete it->second.variable;
|
||||
break;
|
||||
}
|
||||
SymbolDatabase::~SymbolDatabase() {
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
delete sdb->scan_queue;
|
||||
delete sdb->symbols;
|
||||
|
||||
xe_memory_release(sdb->memory);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
xe_sdb_ref xe_sdb_retain(xe_sdb_ref sdb) {
|
||||
xe_ref_retain((xe_ref)sdb);
|
||||
return sdb;
|
||||
int SymbolDatabase::Analyze() {
|
||||
// Iteratively run passes over the db.
|
||||
// This uses a queue to do a breadth-first search of all accessible
|
||||
// functions. Callbacks and such likely won't be hit.
|
||||
|
||||
const xe_xex2_header_t *header = module_->xex_header();
|
||||
|
||||
// Find __savegprlr_* and __restgprlr_*.
|
||||
FindGplr();
|
||||
|
||||
// Add each import thunk.
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
AddImports(&header->import_libraries[n]);
|
||||
}
|
||||
|
||||
// Add each export root.
|
||||
// TODO(benvanik): exports.
|
||||
// - insert fn or variable
|
||||
// - queue fn
|
||||
|
||||
// Add method hints, if available.
|
||||
// Not all XEXs have these.
|
||||
AddMethodHints();
|
||||
|
||||
// Queue entry point of the application.
|
||||
FunctionSymbol* fn = GetOrInsertFunction(header->exe_entry_point);
|
||||
fn->name = strdup("<entry>");
|
||||
|
||||
// Keep pumping the queue until there's nothing left to do.
|
||||
FlushQueue();
|
||||
|
||||
// Do a pass over the functions to fill holes.
|
||||
FillHoles();
|
||||
FlushQueue();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_sdb_release(xe_sdb_ref sdb) {
|
||||
xe_ref_release((xe_ref)sdb, (xe_ref_dealloc_t)xe_sdb_dealloc);
|
||||
}
|
||||
|
||||
xe_sdb_function_t* xe_sdb_insert_function(xe_sdb_ref sdb, uint32_t address) {
|
||||
xe_sdb_function_t *fn = xe_sdb_get_function(sdb, address);
|
||||
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
||||
FunctionSymbol* fn = GetFunction(address);
|
||||
if (fn) {
|
||||
return fn;
|
||||
}
|
||||
|
||||
printf("add fn %.8X\n", address);
|
||||
fn = (xe_sdb_function_t*)xe_calloc(sizeof(xe_sdb_function_t));
|
||||
fn = new FunctionSymbol();
|
||||
fn->start_address = address;
|
||||
xe_sdb_symbol_t symbol;
|
||||
symbol.type = 0;
|
||||
symbol.function = fn;
|
||||
sdb->function_count++;
|
||||
sdb->symbols->insert(xe_sdb_symbol_map::value_type(address, symbol));
|
||||
sdb->scan_queue->push_back(fn);
|
||||
function_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, fn));
|
||||
scan_queue_.push_back(fn);
|
||||
return fn;
|
||||
}
|
||||
|
||||
xe_sdb_variable_t* xe_sdb_insert_variable(xe_sdb_ref sdb, uint32_t address) {
|
||||
xe_sdb_variable_t *var = xe_sdb_get_variable(sdb, address);
|
||||
VariableSymbol* SymbolDatabase::GetOrInsertVariable(uint32_t address) {
|
||||
VariableSymbol* var = GetVariable(address);
|
||||
if (var) {
|
||||
return var;
|
||||
}
|
||||
|
||||
printf("add var %.8X\n", address);
|
||||
var = (xe_sdb_variable_t*)xe_calloc(sizeof(xe_sdb_variable_t));
|
||||
var = new VariableSymbol();
|
||||
var->address = address;
|
||||
xe_sdb_symbol_t symbol;
|
||||
symbol.type = 1;
|
||||
symbol.variable = var;
|
||||
sdb->variable_count++;
|
||||
sdb->symbols->insert(xe_sdb_symbol_map::value_type(address, symbol));
|
||||
variable_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, var));
|
||||
return var;
|
||||
}
|
||||
|
||||
xe_sdb_function_t* xe_sdb_get_function(xe_sdb_ref sdb, uint32_t address) {
|
||||
xe_sdb_symbol_map::iterator i = sdb->symbols->find(address);
|
||||
if (i != sdb->symbols->end() &&
|
||||
i->second.type == 0) {
|
||||
return i->second.function;
|
||||
FunctionSymbol* SymbolDatabase::GetFunction(uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Function) {
|
||||
return static_cast<FunctionSymbol*>(i->second);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
xe_sdb_variable_t* xe_sdb_get_variable(xe_sdb_ref sdb, uint32_t address) {
|
||||
xe_sdb_symbol_map::iterator i = sdb->symbols->find(address);
|
||||
if (i != sdb->symbols->end() &&
|
||||
i->second.type == 1) {
|
||||
return i->second.variable;
|
||||
VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) {
|
||||
SymbolMap::iterator i = symbols_.find(address);
|
||||
if (i != symbols_.end() && i->second->symbol_type == Symbol::Variable) {
|
||||
return static_cast<VariableSymbol*>(i->second);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int xe_sdb_get_functions(xe_sdb_ref sdb, xe_sdb_function_t ***out_functions,
|
||||
size_t *out_function_count) {
|
||||
xe_sdb_function_t **functions = (xe_sdb_function_t**)xe_malloc(
|
||||
sizeof(xe_sdb_function_t*) * sdb->function_count);
|
||||
int n = 0;
|
||||
for (xe_sdb_symbol_map::iterator it = sdb->symbols->begin();
|
||||
it != sdb->symbols->end(); ++it) {
|
||||
switch (it->second.type) {
|
||||
case 0:
|
||||
functions[n++] = it->second.function;
|
||||
break;
|
||||
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
if (it->second->symbol_type == Symbol::Function) {
|
||||
functions.push_back(static_cast<FunctionSymbol*>(it->second));
|
||||
}
|
||||
}
|
||||
*out_functions = functions;
|
||||
*out_function_count = sdb->function_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_sdb_dump(xe_sdb_ref sdb) {
|
||||
void SymbolDatabase::Dump() {
|
||||
uint32_t previous = 0;
|
||||
for (xe_sdb_symbol_map::iterator it = sdb->symbols->begin();
|
||||
it != sdb->symbols->end(); ++it) {
|
||||
switch (it->second.type) {
|
||||
case 0:
|
||||
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
|
||||
switch (it->second->symbol_type) {
|
||||
case Symbol::Function:
|
||||
{
|
||||
xe_sdb_function_t *fn = it->second.function;
|
||||
FunctionSymbol* fn = static_cast<FunctionSymbol*>(it->second);
|
||||
if (previous && (int)(fn->start_address - previous) > 0) {
|
||||
printf("%.8X-%.8X (%5d) h\n", previous, fn->start_address,
|
||||
fn->start_address - previous);
|
||||
@@ -169,9 +142,9 @@ void xe_sdb_dump(xe_sdb_ref sdb) {
|
||||
previous = fn->end_address + 4;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
case Symbol::Variable:
|
||||
{
|
||||
xe_sdb_variable_t *var = it->second.variable;
|
||||
VariableSymbol* var = static_cast<VariableSymbol*>(it->second);
|
||||
printf("%.8X v %s\n", var->address,
|
||||
var->name ? var->name : "<unknown>");
|
||||
}
|
||||
@@ -180,7 +153,7 @@ void xe_sdb_dump(xe_sdb_ref sdb) {
|
||||
}
|
||||
}
|
||||
|
||||
int xe_sdb_find_gplr(xe_sdb_ref sdb, xe_module_ref module) {
|
||||
int SymbolDatabase::FindGplr() {
|
||||
// Special stack save/restore functions.
|
||||
// __savegprlr_14 to __savegprlr_31
|
||||
// __restgprlr_14 to __restgprlr_31
|
||||
@@ -232,16 +205,16 @@ int xe_sdb_find_gplr(xe_sdb_ref sdb, xe_module_ref module) {
|
||||
};
|
||||
|
||||
uint32_t gplr_start = 0;
|
||||
const xe_xex2_header_t *header = xe_module_get_xex_header(module);
|
||||
const xe_xex2_header_t* header = module_->xex_header();
|
||||
for (size_t n = 0, i = 0; n < header->section_count; n++) {
|
||||
const xe_xex2_section_t *section = &header->sections[n];
|
||||
const size_t start_address = header->exe_address +
|
||||
(i * xe_xex2_section_length);
|
||||
const size_t end_address = start_address + (section->info.page_count *
|
||||
xe_xex2_section_length);
|
||||
const xe_xex2_section_t* section = &header->sections[n];
|
||||
const size_t start_address =
|
||||
header->exe_address + (i * xe_xex2_section_length);
|
||||
const size_t end_address =
|
||||
start_address + (section->info.page_count * xe_xex2_section_length);
|
||||
if (section->info.type == XEX_SECTION_CODE) {
|
||||
gplr_start = xe_memory_search_aligned(
|
||||
sdb->memory, start_address, end_address,
|
||||
memory_, start_address, end_address,
|
||||
code_values, XECOUNT(code_values));
|
||||
if (gplr_start) {
|
||||
break;
|
||||
@@ -255,81 +228,82 @@ int xe_sdb_find_gplr(xe_sdb_ref sdb, xe_module_ref module) {
|
||||
|
||||
// Add function stubs.
|
||||
char name[32];
|
||||
uint32_t addr = gplr_start;
|
||||
uint32_t address = gplr_start;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintf(name, XECOUNT(name), "__savegprlr_%d", n);
|
||||
xe_sdb_function_t *fn = xe_sdb_insert_function(sdb, addr);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 2 * 4;
|
||||
fn->name = xestrdup(name);
|
||||
fn->type = kXESDBFunctionUser;
|
||||
addr += 4;
|
||||
fn->type = FunctionSymbol::User;
|
||||
address += 4;
|
||||
}
|
||||
addr = gplr_start + 20 * 4;
|
||||
address = gplr_start + 20 * 4;
|
||||
for (int n = 14; n <= 31; n++) {
|
||||
xesnprintf(name, XECOUNT(name), "__restgprlr_%d", n);
|
||||
xe_sdb_function_t *fn = xe_sdb_insert_function(sdb, addr);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(address);
|
||||
fn->end_address = fn->start_address + (31 - n) * 4 + 3 * 4;
|
||||
fn->name = xestrdup(name);
|
||||
fn->type = kXESDBFunctionUser;
|
||||
addr += 4;
|
||||
fn->type = FunctionSymbol::User;
|
||||
address += 4;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_sdb_add_imports(xe_sdb_ref sdb, xe_module_ref module,
|
||||
const xe_xex2_import_library_t *library) {
|
||||
xe_xex2_ref xex = xe_module_get_xex(module);
|
||||
xe_xex2_import_info_t *import_infos;
|
||||
int SymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
|
||||
xe_xex2_ref xex = module_->xex();
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
if (xe_xex2_get_import_infos(xex, library, &import_infos,
|
||||
&import_info_count)) {
|
||||
xe_xex2_release(xex);
|
||||
return 1;
|
||||
}
|
||||
|
||||
char name[64];
|
||||
for (size_t n = 0; n < import_info_count; n++) {
|
||||
const xe_xex2_import_info_t *info = &import_infos[n];
|
||||
xe_sdb_variable_t *var = xe_sdb_insert_variable(sdb, info->value_address);
|
||||
const xe_xex2_import_info_t* info = &import_infos[n];
|
||||
VariableSymbol* var = GetOrInsertVariable(info->value_address);
|
||||
// TODO(benvanik): use kernel name
|
||||
xesnprintf(name, XECOUNT(name), "__var_%s_%.3X", library->name,
|
||||
info->ordinal);
|
||||
var->name = strdup(name);
|
||||
if (info->thunk_address) {
|
||||
xe_sdb_function_t *fn = xe_sdb_insert_function(sdb, info->thunk_address);
|
||||
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
|
||||
// TODO(benvanik): use kernel name
|
||||
xesnprintf(name, XECOUNT(name), "__thunk_%s_%.3X", library->name,
|
||||
info->ordinal);
|
||||
fn->end_address = fn->start_address + 16 - 4;
|
||||
fn->name = strdup(name);
|
||||
fn->type = kXESDBFunctionKernel;
|
||||
fn->type = FunctionSymbol::Kernel;
|
||||
}
|
||||
}
|
||||
|
||||
xe_xex2_release(xex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_sdb_add_method_hints(xe_sdb_ref sdb, xe_module_ref module) {
|
||||
xe_module_pe_method_info_t *method_infos;
|
||||
int SymbolDatabase::AddMethodHints() {
|
||||
PEMethodInfo* method_infos;
|
||||
size_t method_info_count;
|
||||
if (xe_module_get_method_hints(module, &method_infos, &method_info_count)) {
|
||||
if (module_->GetMethodHints(&method_infos, &method_info_count)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < method_info_count; n++) {
|
||||
xe_module_pe_method_info_t *method_info = &method_infos[n];
|
||||
xe_sdb_function_t *fn = xe_sdb_insert_function(sdb, method_info->address);
|
||||
PEMethodInfo* method_info = &method_infos[n];
|
||||
FunctionSymbol* fn = GetOrInsertFunction(method_info->address);
|
||||
fn->end_address = method_info->address + method_info->total_length - 4;
|
||||
fn->type = kXESDBFunctionUser;
|
||||
fn->type = FunctionSymbol::User;
|
||||
// TODO(benvanik): something with prolog_length?
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_sdb_analyze_function(xe_sdb_ref sdb, xe_sdb_function_t *fn) {
|
||||
int SymbolDatabase::AnalyzeFunction(FunctionSymbol* fn) {
|
||||
// Ignore functions already analyzed.
|
||||
if (fn->type != kXESDBFunctionUnknown) {
|
||||
if (fn->type != FunctionSymbol::Unknown) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -339,52 +313,21 @@ int xe_sdb_analyze_function(xe_sdb_ref sdb, xe_sdb_function_t *fn) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_sdb_flush_queue(xe_sdb_ref sdb) {
|
||||
while (sdb->scan_queue->size()) {
|
||||
xe_sdb_function_t *fn = sdb->scan_queue->front();
|
||||
sdb->scan_queue->pop_front();
|
||||
if (!xe_sdb_analyze_function(sdb, fn)) {
|
||||
int SymbolDatabase::FlushQueue() {
|
||||
while (scan_queue_.size()) {
|
||||
FunctionSymbol* fn = scan_queue_.front();
|
||||
scan_queue_.pop_front();
|
||||
if (!AnalyzeFunction(fn)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_sdb_analyze_module(xe_sdb_ref sdb, xe_module_ref module) {
|
||||
// Iteratively run passes over the db.
|
||||
// This uses a queue to do a breadth-first search of all accessible
|
||||
// functions. Callbacks and such likely won't be hit.
|
||||
|
||||
const xe_xex2_header_t *header = xe_module_get_xex_header(module);
|
||||
|
||||
// Find __savegprlr_* and __restgprlr_*.
|
||||
xe_sdb_find_gplr(sdb, module);
|
||||
|
||||
// Add each import thunk.
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t *library = &header->import_libraries[n];
|
||||
xe_sdb_add_imports(sdb, module, library);
|
||||
}
|
||||
|
||||
// Add each export root.
|
||||
// TODO(benvanik): exports.
|
||||
// - insert fn or variable
|
||||
// - queue fn
|
||||
|
||||
// Add method hints, if available.
|
||||
// Not all XEXs have these.
|
||||
xe_sdb_add_method_hints(sdb, module);
|
||||
|
||||
// Queue entry point of the application.
|
||||
xe_sdb_function_t *fn = xe_sdb_insert_function(sdb, header->exe_entry_point);
|
||||
fn->name = strdup("<entry>");
|
||||
|
||||
// Keep pumping the queue until there's nothing left to do.
|
||||
xe_sdb_flush_queue(sdb);
|
||||
|
||||
// Do a pass over the functions to fill holes.
|
||||
// TODO(benvanik): hole filling.
|
||||
xe_sdb_flush_queue(sdb);
|
||||
|
||||
int SymbolDatabase::FillHoles() {
|
||||
// TODO(benvanik): scan all holes
|
||||
// If 4b, check if 0x00000000 and ignore (alignment padding)
|
||||
// If 8b, check if first value is within .text and ignore (EH entry)
|
||||
// Else, add to scan queue as function?
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
{
|
||||
'sources': [
|
||||
'codegen.cc',
|
||||
'cpu.cc',
|
||||
'exec_module.cc',
|
||||
'processor.cc',
|
||||
'sdb.cc',
|
||||
],
|
||||
|
||||
|
||||
Reference in New Issue
Block a user