/** ****************************************************************************** * 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 #include #include #include #include #include #include #include #include #include #include #include #include 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 functions; if (sdb->GetAllFunctions(functions)) { return 1; } int result_code = 0; for (std::vector::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::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; }