Files
Xenia-Canary/src/xenia/cpu/llvmbe/llvm_jit.cc

212 lines
6.2 KiB
C++

/**
******************************************************************************
* 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/llvmbe/llvm_jit.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/JIT.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <xenia/cpu/exec_module.h>
#include <xenia/cpu/llvmbe/llvm_code_unit_builder.h>
#include <xenia/cpu/llvmbe/llvm_exports.h>
#include <xenia/cpu/sdb.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::llvmbe;
using namespace xe::cpu::sdb;
LLVMJIT::LLVMJIT(xe_memory_ref memory, FunctionTable* fn_table) :
JIT(memory, fn_table),
context_(NULL), engine_(NULL), module_(NULL), cub_(NULL) {
}
LLVMJIT::~LLVMJIT() {
delete cub_;
if (engine_) {
engine_->removeModule(module_);
}
delete engine_;
delete context_;
}
//namespace {
//void* LazyFunctionCreator(const std::string& name) {
// printf("lazy: %s", name.c_str());
// return NULL;
//}
//}
int LLVMJIT::Setup() {
int result_code = 1;
std::string error_message;
context_ = new LLVMContext();
// Create a shared code unit builder used while JITing.
// Since there's only one we'll need to lock when generating code.
// In the future we could try to generate new code in separate CUBs and then
// link into the main module under the lock.
cub_ = new LLVMCodeUnitBuilder(memory_, context_);
result_code = cub_->Init("jit", "");
if (result_code) {
return result_code;
}
module_ = cub_->module();
EngineBuilder builder(module_);
builder.setEngineKind(EngineKind::JIT);
builder.setErrorStr(&error_message);
builder.setOptLevel(CodeGenOpt::None);
//builder.setOptLevel(CodeGenOpt::Aggressive);
//builder.setJITMemoryManager(jmm);
//builder.setTargetOptions();
builder.setAllocateGVsWithCode(false);
//builder.setUseMCJIT(true);
engine_ = builder.create();
XEEXPECTNOTNULL(engine_);
//engine_->DisableSymbolSearching();
//engine_->InstallLazyFunctionCreator(LazyFunctionCreator);
XEEXPECTZERO(InjectGlobals());
result_code = 0;
XECLEANUP:
return result_code;
}
int LLVMJIT::InjectGlobals() {
LLVMContext& context = *context_;
const DataLayout* dl = engine_->getDataLayout();
Type* intPtrTy = dl->getIntPtrType(context);
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
GlobalVariable* gv;
// xe_memory_base
// This is the base void* pointer to the memory space.
gv = new GlobalVariable(
*module_,
int8PtrTy,
true,
GlobalValue::ExternalLinkage,
0,
"xe_memory_base");
// Align to 64b - this makes SSE faster.
gv->setAlignment(64);
gv->setInitializer(ConstantExpr::getIntToPtr(
ConstantInt::get(intPtrTy, (uintptr_t)xe_memory_addr(memory_, 0)),
int8PtrTy));
// Setup global exports (the Xe* functions called by generated code).
GlobalExports global_exports;
cpu::GetGlobalExports(&global_exports);
SetupLlvmExports(&global_exports, module_, dl, engine_);
return 0;
}
int LLVMJIT::InitModule(ExecModule* module) {
SymbolDatabase* sdb = module->sdb();
Module* cub_module = cub_->module();
// Setup all imports (as needed).
std::vector<FunctionSymbol*> functions;
if (sdb->GetAllFunctions(functions)) {
return 1;
}
int result_code = 0;
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
it != functions.end(); ++it) {
FunctionSymbol* symbol = *it;
if (symbol->type == FunctionSymbol::Kernel) {
// Generate the function.
Function* fn = NULL;
result_code = cub_->MakeFunction(symbol, &fn);
if (result_code) {
XELOGE("Unable to generate import %s", symbol->name());
return result_code;
}
// Set global mappings for shim and data.
char shim_name[256];
xesnprintfa(shim_name, XECOUNT(shim_name),
"__shim_%s", symbol->kernel_export->name);
Function* shim = cub_module->getFunction(shim_name);
if (shim) {
engine_->updateGlobalMapping(
shim, (void*)symbol->kernel_export->function_data.shim);
}
char shim_data_name[256];
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
"__shim_data_%s", symbol->kernel_export->name);
GlobalVariable* shim_data = cub_module->getGlobalVariable(shim_data_name);
if (shim_data) {
engine_->updateGlobalMapping(
shim_data, (void*)symbol->kernel_export->function_data.shim_data);
}
}
}
return 0;
}
int LLVMJIT::UninitModule(ExecModule* module) {
return 0;
}
FunctionPointer LLVMJIT::GenerateFunction(FunctionSymbol* symbol) {
// In theory this is only called when required, so just assume we need to
// generate it.
// Lock the function for adding.
// If this fails it means the function exists and we don't need to generate
// it.
FunctionPointer ptr = fn_table_->BeginAddFunction(symbol->start_address);
if (ptr) {
return ptr;
}
printf("generating %s...\n", symbol->name());
Function* fn = NULL;
int result_code = cub_->MakeFunction(symbol, &fn);
if (result_code) {
XELOGE("Unable to generate function %s", symbol->name());
return NULL;
}
// LLVM requires all functions called to be defined at the time of
// getPointerToFunction, so this does a recursive depth-first walk
// to generate them.
for (std::vector<FunctionCall>::iterator it = symbol->outgoing_calls.begin();
it != symbol->outgoing_calls.end(); ++it) {
FunctionSymbol* target = it->target;
printf("needs dep fn %s\n", target->name());
GenerateFunction(target);
}
// Generate the machine code.
ptr = (FunctionPointer)engine_->getPointerToFunction(fn);
// Add to the function table.
fn_table_->AddFunction(symbol->start_address, ptr);
return ptr;
}