Converting everything to C++ cause I'm a masochist.

This commit is contained in:
Ben Vanik
2013-01-20 01:13:59 -08:00
parent 8a5dcbc1dd
commit ca2908db32
47 changed files with 3966 additions and 3760 deletions

View File

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

View File

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

View File

@@ -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
View 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();
}

View File

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

View File

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

View File

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

View File

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

View File

@@ -2,7 +2,8 @@
{
'sources': [
'codegen.cc',
'cpu.cc',
'exec_module.cc',
'processor.cc',
'sdb.cc',
],