Broken, incomplete, but need to move forward with rewrite.
This commit is contained in:
parent
4987147055
commit
b018b6fe56
28
TODO.md
28
TODO.md
|
@ -1,23 +1,13 @@
|
|||
refactor:
|
||||
module gen
|
||||
recompiler (move to cpu)
|
||||
implement:
|
||||
function table
|
||||
exec_module FindFunctionSymbol
|
||||
|
||||
|
||||
|
||||
ExecModule:
|
||||
Prepare(memory, export_resolver)
|
||||
Init()
|
||||
Uninit()
|
||||
Execute(ppc_state, addr)
|
||||
|
||||
JITExecModule:
|
||||
|
||||
LibraryExecModule:
|
||||
|
||||
|
||||
Processor::Execute(addr):
|
||||
// TODO: faster search, if needed
|
||||
for each exec_module:
|
||||
if (exec_module->Execute(addr)) {
|
||||
break;
|
||||
}
|
||||
jit:
|
||||
- add imports/exports/etc to module
|
||||
- function generator (thread safe)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -28,7 +28,7 @@ xe_pal_mac_t* pal;
|
|||
|
||||
void xe_pal_dealloc();
|
||||
int xe_pal_init(xe_pal_options_t options) {
|
||||
pal = (xe_pal_mac_t)xe_calloc(sizeof(pal));
|
||||
pal = (xe_pal_mac_t*)xe_calloc(sizeof(pal));
|
||||
|
||||
mach_timebase_info_data_t info;
|
||||
mach_timebase_info(&info);
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_BACKEND_H_
|
||||
#define XENIA_CPU_BACKEND_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class CodeUnitBuilder;
|
||||
class FunctionTable;
|
||||
class JIT;
|
||||
class LibraryLinker;
|
||||
class LibraryLoader;
|
||||
|
||||
|
||||
class Backend {
|
||||
public:
|
||||
virtual ~Backend() {}
|
||||
|
||||
virtual CodeUnitBuilder* CreateCodeUnitBuilder() = 0;
|
||||
virtual LibraryLinker* CreateLibraryLinker() = 0;
|
||||
virtual LibraryLoader* CreateLibraryLoader() = 0;
|
||||
|
||||
virtual JIT* CreateJIT(xe_memory_ref memory, FunctionTable* fn_table) = 0;
|
||||
|
||||
protected:
|
||||
Backend() {}
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_BACKEND_H_
|
|
@ -0,0 +1,51 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_CODE_UNIT_BUILDER_H_
|
||||
#define XENIA_CPU_CODE_UNIT_BUILDER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class CodeUnitBuilder {
|
||||
public:
|
||||
virtual ~CodeUnitBuilder() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
virtual int Init(const char* module_name, const char* module_path) = 0;
|
||||
virtual int MakeFunction(sdb::FunctionSymbol* symbol) = 0;
|
||||
virtual int Finalize() = 0;
|
||||
virtual void Reset() = 0;
|
||||
|
||||
// TODO(benvanik): write to file/etc
|
||||
|
||||
protected:
|
||||
CodeUnitBuilder(xe_memory_ref memory) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
}
|
||||
|
||||
xe_memory_ref memory_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODE_UNIT_BUILDER_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -28,96 +28,6 @@
|
|||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ModuleGenerator::ModuleGenerator(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
const char* module_name, const char* module_path, SymbolDatabase* sdb,
|
||||
LLVMContext* context, Module* gen_module, ExecutionEngine* engine) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_name_ = xestrdupa(module_name);
|
||||
module_path_ = xestrdupa(module_path);
|
||||
sdb_ = sdb;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
engine_ = engine;
|
||||
di_builder_ = NULL;
|
||||
}
|
||||
|
||||
ModuleGenerator::~ModuleGenerator() {
|
||||
for (std::map<uint32_t, CodegenFunction*>::iterator it =
|
||||
functions_.begin(); it != functions_.end(); ++it) {
|
||||
delete it->second;
|
||||
}
|
||||
|
||||
delete di_builder_;
|
||||
xe_free(module_path_);
|
||||
xe_free(module_name_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ModuleGenerator::Generate() {
|
||||
std::string 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.
|
||||
char dir[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
|
||||
char* slash = xestrrchra(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
di_builder_ = new DIBuilder(*gen_module_);
|
||||
di_builder_->createCompileUnit(
|
||||
dwarf::DW_LANG_C99, //0x8010,
|
||||
StringRef(module_name_),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
// Add export wrappers.
|
||||
//
|
||||
|
||||
// Add all functions.
|
||||
// We do two passes - the first creates the function signature and global
|
||||
// value (so that we can call it), the second actually builds the function.
|
||||
std::vector<FunctionSymbol*> functions;
|
||||
if (!sdb_->GetAllFunctions(functions)) {
|
||||
XELOGI("Beginning prep of %ld functions...", functions.size());
|
||||
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
|
||||
it != functions.end(); ++it) {
|
||||
FunctionSymbol* fn = *it;
|
||||
switch (fn->type) {
|
||||
case FunctionSymbol::User:
|
||||
PrepareFunction(fn);
|
||||
break;
|
||||
case FunctionSymbol::Kernel:
|
||||
if (fn->kernel_export && fn->kernel_export->is_implemented) {
|
||||
AddPresentImport(fn);
|
||||
} else {
|
||||
AddMissingImport(fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
break;
|
||||
}
|
||||
}
|
||||
XELOGI("Function prep complete");
|
||||
}
|
||||
|
||||
// Build out all the user functions.
|
||||
size_t n = 0;
|
||||
XELOGI("Beginning generation of %ld functions...", functions.size());
|
||||
|
@ -143,55 +53,6 @@ void ModuleGenerator::AddFunctionsToMap(
|
|||
}
|
||||
}
|
||||
|
||||
ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
|
||||
uint32_t address) {
|
||||
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
|
||||
if (it != functions_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Function* ModuleGenerator::CreateFunctionDefinition(const char* name) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
std::vector<Type*> args;
|
||||
args.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
|
||||
args.push_back(Type::getInt64Ty(context));
|
||||
Type* return_type = Type::getVoidTy(context);
|
||||
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function* f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(name), ft));
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
// Indicate that the function will never be unwound with an exception.
|
||||
// If we ever support native exception handling we may need to remove this.
|
||||
f->doesNotThrow();
|
||||
|
||||
// May be worth trying the X86_FastCall, as we only need state in a register.
|
||||
//f->setCallingConv(CallingConv::Fast);
|
||||
f->setCallingConv(CallingConv::C);
|
||||
|
||||
Function::arg_iterator fn_args = f->arg_begin();
|
||||
// 'state'
|
||||
Value* fn_arg = fn_args++;
|
||||
fn_arg->setName("state");
|
||||
f->setDoesNotAlias(1);
|
||||
f->setDoesNotCapture(1);
|
||||
// 'state' should try to be in a register, if possible.
|
||||
// TODO(benvanik): verify that's a good idea.
|
||||
// f->getArgumentList().begin()->addAttr(
|
||||
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
|
||||
|
||||
// 'lr'
|
||||
fn_arg = fn_args++;
|
||||
fn_arg->setName("lr");
|
||||
|
||||
return f;
|
||||
};
|
||||
|
||||
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||
Module *m = gen_module_;
|
||||
|
@ -288,48 +149,3 @@ void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
|||
|
||||
OptimizeFunction(m, f);
|
||||
}
|
||||
|
||||
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* f = CreateFunctionDefinition(fn->name());
|
||||
|
||||
// Setup our codegen wrapper to keep all the pointers together.
|
||||
CodegenFunction* cgf = new CodegenFunction();
|
||||
cgf->symbol = fn;
|
||||
cgf->function_type = f->getFunctionType();
|
||||
cgf->function = f;
|
||||
functions_.insert(std::pair<uint32_t, CodegenFunction*>(
|
||||
fn->start_address, cgf));
|
||||
}
|
||||
|
||||
void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
|
||||
FunctionSymbol* fn = cgf->symbol;
|
||||
|
||||
// Setup the generation context.
|
||||
FunctionGenerator fgen(
|
||||
memory_, sdb_, fn, context_, gen_module_, cgf->function);
|
||||
|
||||
// Run through and generate each basic block.
|
||||
fgen.GenerateBasicBlocks();
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
OptimizeFunction(gen_module_, cgf->function);
|
||||
}
|
||||
|
||||
void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
|
||||
FunctionPassManager pm(m);
|
||||
//fn->dump();
|
||||
if (FLAGS_optimize_ir_functions) {
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(pm);
|
||||
}
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*fn);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/codegen/recompiler.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/Constants.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/cpu-private.h>
|
||||
#include <xenia/cpu/llvm_exports.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/codegen/module_generator.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/xethunk/xethunk.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
Recompiler::Recompiler(
|
||||
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
||||
shared_ptr<SymbolDatabase> sdb, const char* module_name) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
sdb_ = sdb;
|
||||
}
|
||||
|
||||
Recompiler::~Recompiler() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int Recompiler::Process() {
|
||||
// Check to see if a cached result exists on disk - if so, use it.
|
||||
if (!LoadLibrary(library_path)) {
|
||||
// Succeeded - done!
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Generate all the code and dump it to code units.
|
||||
// This happens in multiple threads.
|
||||
if (GenerateCodeUnits()) {
|
||||
XELOGCPU("Failed to generate code units for module");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Link all of the generated code units. This runs any link-time optimizations
|
||||
// and other per-library operations.
|
||||
if (LinkCodeUnits()) {
|
||||
XELOGCPU("Failed to link code units");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load the built library now.
|
||||
if (LoadLibrary(library_path)) {
|
||||
XELOGCPU("Failed to load the generated library");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Recompiler::GenerateCodeUnits() {
|
||||
xe_system_info sys_info;
|
||||
XEEXPECTZERO(xe_pal_get_system_info(&sys_info));
|
||||
// sys_info.processors.physical_count;
|
||||
// sys_info.processors.logical_count;
|
||||
|
||||
// Queue up all functions to process.
|
||||
|
||||
// Spawn worker threads to process the queue.
|
||||
|
||||
// Wait until all threads complete.
|
||||
|
||||
return 0;
|
||||
|
||||
XECLEANUP:
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Recompiler::LinkCodeUnits() {
|
||||
// Invoke linker.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Recompiler::LoadLibrary(const char* path) {
|
||||
// Check file exists.
|
||||
|
||||
// TODO(benvanik): version check somehow?
|
||||
|
||||
// Load library.
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_RECOMPILER_H_
|
||||
#define XENIA_CPU_CODEGEN_RECOMPILER_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
|
||||
|
||||
class Recompiler {
|
||||
public:
|
||||
Recompiler(
|
||||
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
|
||||
shared_ptr<sdb::SymbolDatabase> sdb, const char* module_name);
|
||||
~Recompiler();
|
||||
|
||||
int Process();
|
||||
|
||||
private:
|
||||
int GenerateCodeUnits();
|
||||
int LinkCodeUnits();
|
||||
int LoadLibrary(const char* path);
|
||||
|
||||
xechar_t* library_path_;
|
||||
};
|
||||
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_RECOMPILER_H_
|
|
@ -1,14 +0,0 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'emit.h',
|
||||
'emit_alu.cc',
|
||||
'emit_control.cc',
|
||||
'emit_fpu.cc',
|
||||
'emit_memory.cc',
|
||||
'function_generator.cc',
|
||||
'function_generator.h',
|
||||
'module_generator.cc',
|
||||
'module_generator.h',
|
||||
],
|
||||
}
|
|
@ -12,4 +12,7 @@
|
|||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
// TODO(benvanik): conditionally include?
|
||||
#include <xenia/cpu/llvmbe/llvm_backend.h>
|
||||
|
||||
#endif // XENIA_CPU_CPU_H_
|
||||
|
|
|
@ -9,67 +9,49 @@
|
|||
|
||||
#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/Constants.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/cpu-private.h>
|
||||
#include <xenia/cpu/llvm_exports.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/codegen/module_generator.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/xethunk/xethunk.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ExecModule::ExecModule(
|
||||
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
||||
const char* module_name, const char* module_path,
|
||||
shared_ptr<llvm::ExecutionEngine>& engine) {
|
||||
FunctionTable* fn_table,
|
||||
const char* module_name, const char* module_path) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
fn_table_ = fn_table;
|
||||
module_name_ = xestrdupa(module_name);
|
||||
module_path_ = xestrdupa(module_path);
|
||||
engine_ = engine;
|
||||
|
||||
context_ = shared_ptr<LLVMContext>(new LLVMContext());
|
||||
}
|
||||
|
||||
ExecModule::~ExecModule() {
|
||||
if (gen_module_) {
|
||||
Uninit();
|
||||
engine_->removeModule(gen_module_.get());
|
||||
}
|
||||
|
||||
xe_free(module_path_);
|
||||
xe_free(module_name_);
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ExecModule::PrepareXex(xe_xex2_ref xex) {
|
||||
SymbolDatabase* ExecModule::sdb() {
|
||||
return sdb_.get();
|
||||
}
|
||||
|
||||
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
|
||||
start_address, end_address));
|
||||
|
||||
code_addr_low_ = start_address;
|
||||
code_addr_high_ = end_address;
|
||||
|
||||
return Prepare();
|
||||
}
|
||||
|
||||
int ExecModule::PrepareXexModule(xe_xex2_ref xex) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
|
||||
|
||||
|
@ -94,196 +76,45 @@ int ExecModule::PrepareXex(xe_xex2_ref xex) {
|
|||
return result_code;
|
||||
}
|
||||
|
||||
// Import variables.
|
||||
// TODO??
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
|
||||
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
|
||||
start_address, end_address));
|
||||
|
||||
code_addr_low_ = start_address;
|
||||
code_addr_high_ = end_address;
|
||||
|
||||
return Prepare();
|
||||
}
|
||||
|
||||
int ExecModule::Prepare() {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
char file_name[XE_MAX_PATH];
|
||||
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
auto_ptr<Module> shared_module;
|
||||
auto_ptr<raw_ostream> outs;
|
||||
// Analyze the module and add its symbols to the symbol database.
|
||||
// This always happens, even if we have a cached copy of the library, as
|
||||
// we may end up needing this information later.
|
||||
// TODO(benvanik): see how much memory this is using - it may be worth
|
||||
// dropping and keeping around a smaller structure for future lookups
|
||||
// instead.
|
||||
XEEXPECTZERO(sdb_->Analyze());
|
||||
|
||||
PassManager pm;
|
||||
PassManagerBuilder pmb;
|
||||
|
||||
// TODO(benvanik): embed the bc file into the emulator.
|
||||
const char *thunk_path = "src/xenia/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());
|
||||
|
||||
// Load a specified module map and diff.
|
||||
if (FLAGS_load_module_map.size()) {
|
||||
sdb_->ReadMap(FLAGS_load_module_map.c_str());
|
||||
}
|
||||
|
||||
// Dump the symbol database.
|
||||
if (FLAGS_dump_module_map) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s.map", FLAGS_dump_path.c_str(), module_name_);
|
||||
sdb_->WriteMap(file_name);
|
||||
}
|
||||
|
||||
// Initialize the module.
|
||||
gen_module_ = shared_ptr<Module>(
|
||||
new Module(module_name_, *context_.get()));
|
||||
// TODO(benavnik): addModuleFlag?
|
||||
|
||||
// Inject globals.
|
||||
// This should be done ASAP to ensure that JITed functions can use the
|
||||
// constant addresses.
|
||||
XEEXPECTZERO(InjectGlobals());
|
||||
|
||||
// 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<ModuleGenerator>(new ModuleGenerator(
|
||||
memory_, export_resolver_.get(), module_name_, module_path_,
|
||||
sdb_.get(), context_.get(), gen_module_.get(),
|
||||
engine_.get()));
|
||||
XEEXPECTZERO(codegen_->Generate());
|
||||
|
||||
// Write to cache.
|
||||
// TODO(benvanik): cache stuff
|
||||
|
||||
// Dump pre-optimized module to disk.
|
||||
if (FLAGS_dump_module_bitcode) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s-preopt.bc", FLAGS_dump_path.c_str(), module_name_);
|
||||
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
|
||||
file_name, error_message, raw_fd_ostream::F_Binary));
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module_.get(), *outs);
|
||||
}
|
||||
// Load a specified module map and diff.
|
||||
if (FLAGS_load_module_map.size()) {
|
||||
sdb_->ReadMap(FLAGS_load_module_map.c_str());
|
||||
}
|
||||
|
||||
// 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()));
|
||||
if (FLAGS_optimize_ir_modules) {
|
||||
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_);
|
||||
|
||||
// Dump post-optimized module to disk.
|
||||
if (FLAGS_optimize_ir_modules && FLAGS_dump_module_bitcode) {
|
||||
// Dump the symbol database.
|
||||
if (FLAGS_dump_module_map) {
|
||||
xesnprintfa(file_name, XECOUNT(file_name),
|
||||
"%s%s.bc", FLAGS_dump_path.c_str(), module_name_);
|
||||
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
|
||||
file_name, error_message, raw_fd_ostream::F_Binary));
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module_.get(), *outs);
|
||||
"%s%s.map", FLAGS_dump_path.c_str(), module_name_);
|
||||
sdb_->WriteMap(file_name);
|
||||
}
|
||||
|
||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
||||
// If recompiling, setup a recompiler and run.
|
||||
// Note that this will just load the library if it's present and valid.
|
||||
// TODO(benvanik): recompiler logic.
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(Init());
|
||||
|
||||
// Force JIT of all functions.
|
||||
// for (Module::iterator it = gen_module_->begin(); it != gen_module_->end();
|
||||
// ++it) {
|
||||
// Function* fn = it;
|
||||
// if (!fn->isDeclaration()) {
|
||||
// engine_->getPointerToFunction(fn);
|
||||
// }
|
||||
// }
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
void ExecModule::AddFunctionsToMap(FunctionMap& map) {
|
||||
codegen_->AddFunctionsToMap(map);
|
||||
}
|
||||
|
||||
int ExecModule::InjectGlobals() {
|
||||
LLVMContext& context = *context_.get();
|
||||
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(
|
||||
*gen_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));
|
||||
|
||||
SetupLlvmExports(gen_module_.get(), dl, engine_.get());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ExecModule::Init() {
|
||||
// Setup all kernel variables.
|
||||
std::vector<VariableSymbol*> variables;
|
||||
|
@ -317,27 +148,11 @@ int ExecModule::Init() {
|
|||
}
|
||||
}
|
||||
|
||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
|
||||
|
||||
// Grab the init function and call it.
|
||||
Function* xe_module_init = gen_module_->getFunction("xe_module_init");
|
||||
std::vector<GenericValue> args;
|
||||
GenericValue ret = engine_->runFunction(xe_module_init, args);
|
||||
|
||||
return static_cast<int>(ret.IntVal.getSExtValue());
|
||||
return 0;
|
||||
}
|
||||
|
||||
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;
|
||||
FunctionSymbol* ExecModule::FindFunctionSymbol(uint32_t address) {
|
||||
return sdb_->GetFunction(address);
|
||||
}
|
||||
|
||||
void ExecModule::Dump() {
|
||||
|
|
|
@ -13,68 +13,46 @@
|
|||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/function_table.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class Function;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
}
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
class ModuleGenerator;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
typedef std::tr1::unordered_map<uint32_t, llvm::Function*> FunctionMap;
|
||||
|
||||
|
||||
class ExecModule {
|
||||
public:
|
||||
ExecModule(
|
||||
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
|
||||
const char* module_name, const char* module_path,
|
||||
shared_ptr<llvm::ExecutionEngine>& engine);
|
||||
FunctionTable* fn_table,
|
||||
const char* module_name, const char* module_path);
|
||||
~ExecModule();
|
||||
|
||||
int PrepareXex(xe_xex2_ref xex);
|
||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
||||
sdb::SymbolDatabase* sdb();
|
||||
|
||||
void AddFunctionsToMap(FunctionMap& map);
|
||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
||||
int PrepareXexModule(xe_xex2_ref xex);
|
||||
|
||||
sdb::FunctionSymbol* FindFunctionSymbol(uint32_t address);
|
||||
|
||||
void Dump();
|
||||
|
||||
private:
|
||||
int Prepare();
|
||||
int InjectGlobals();
|
||||
int Init();
|
||||
int Uninit();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<kernel::ExportResolver> export_resolver_;
|
||||
FunctionTable* fn_table_;
|
||||
char* module_name_;
|
||||
char* module_path_;
|
||||
shared_ptr<llvm::ExecutionEngine> engine_;
|
||||
shared_ptr<sdb::SymbolDatabase> sdb_;
|
||||
shared_ptr<llvm::LLVMContext> context_;
|
||||
shared_ptr<llvm::Module> gen_module_;
|
||||
auto_ptr<codegen::ModuleGenerator> codegen_;
|
||||
|
||||
shared_ptr<sdb::SymbolDatabase> sdb_;
|
||||
uint32_t code_addr_low_;
|
||||
uint32_t code_addr_high_;
|
||||
FunctionMap fns_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,44 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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/function_table.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
FunctionTable::FunctionTable() {
|
||||
}
|
||||
|
||||
FunctionTable::~FunctionTable() {
|
||||
}
|
||||
|
||||
int FunctionTable::AddCodeRange(uint32_t low_address, uint32_t high_address) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionPointer FunctionTable::BeginAddFunction(uint32_t address) {
|
||||
FunctionPointer ptr = map_[address];
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
}
|
||||
map_[address] = reinterpret_cast<FunctionPointer>(0x1);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int FunctionTable::AddFunction(uint32_t address, FunctionPointer ptr) {
|
||||
map_[address] = ptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionPointer FunctionTable::GetFunction(uint32_t address) {
|
||||
FunctionMap::const_iterator it = map_.find(address);
|
||||
return it != map_.end() ? it->second : NULL;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_FUNCTION_TABLE_H_
|
||||
#define XENIA_CPU_FUNCTION_TABLE_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
typedef void (*FunctionPointer)(xe_ppc_state_t*, uint64_t);
|
||||
|
||||
|
||||
class FunctionTable {
|
||||
public:
|
||||
FunctionTable();
|
||||
~FunctionTable();
|
||||
|
||||
int AddCodeRange(uint32_t low_address, uint32_t high_address);
|
||||
FunctionPointer BeginAddFunction(uint32_t address);
|
||||
int AddFunction(uint32_t address, FunctionPointer ptr);
|
||||
FunctionPointer GetFunction(uint32_t address);
|
||||
|
||||
private:
|
||||
typedef std::tr1::unordered_map<uint32_t, FunctionPointer> FunctionMap;
|
||||
FunctionMap map_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_FUNCTION_TABLE_H_
|
|
@ -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/global_exports.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
void XeTrap(xe_ppc_state_t* state, uint32_t cia) {
|
||||
XELOGE("TRAP");
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeIndirectBranch(xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
||||
XELOGCPU("INDIRECT BRANCH %.8X -> %.8X",
|
||||
(uint32_t)br_ia, (uint32_t)target);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeInvalidInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrData i;
|
||||
i.address = cia;
|
||||
i.code = data;
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
|
||||
if (!i.type) {
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X ???",
|
||||
i.address, i.code);
|
||||
} else if (i.type->disassemble) {
|
||||
ppc::InstrDisasm d;
|
||||
i.type->disassemble(i, d);
|
||||
std::string disasm;
|
||||
d.Dump(disasm);
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, disasm.c_str());
|
||||
} else {
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, i.type->name);
|
||||
}
|
||||
}
|
||||
|
||||
void XeAccessViolation(xe_ppc_state_t* state, uint32_t cia, uint64_t ea) {
|
||||
XELOGE("INVALID ACCESS %.8X: tried to touch %.8X",
|
||||
cia, (uint32_t)ea);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeTraceKernelCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
KernelExport* kernel_export) {
|
||||
XELOGCPU("TRACE: %.8X -> k.%.8X (%s)",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia,
|
||||
kernel_export ? kernel_export->name : "unknown");
|
||||
}
|
||||
|
||||
void XeTraceUserCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
FunctionSymbol* fn) {
|
||||
XELOGCPU("TRACE: %.8X -> u.%.8X (%s)",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
||||
}
|
||||
|
||||
void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrType* type = ppc::GetInstrType(data);
|
||||
XELOGCPU("TRACE: %.8X %.8X %s %s",
|
||||
cia, data,
|
||||
type && type->emit ? " " : "X",
|
||||
type ? type->name : "<unknown>");
|
||||
|
||||
// if (cia == 0x82014468) {
|
||||
// printf("BREAKBREAKBREAK\n");
|
||||
// }
|
||||
|
||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void xe::cpu::GetGlobalExports(GlobalExports* global_exports) {
|
||||
global_exports->XeTrap = XeTrap;
|
||||
global_exports->XeIndirectBranch = XeIndirectBranch;
|
||||
global_exports->XeInvalidInstruction = XeInvalidInstruction;
|
||||
global_exports->XeAccessViolation = XeAccessViolation;
|
||||
global_exports->XeTraceKernelCall = XeTraceKernelCall;
|
||||
global_exports->XeTraceUserCall = XeTraceUserCall;
|
||||
global_exports->XeTraceInstruction = XeTraceInstruction;
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_GLOBAL_EXPORTS_H_
|
||||
#define XENIA_CPU_GLOBAL_EXPORTS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/cpu/sdb/symbol.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
typedef struct {
|
||||
void (*XeTrap)(
|
||||
xe_ppc_state_t* state, uint32_t cia);
|
||||
void (*XeIndirectBranch)(
|
||||
xe_ppc_state_t* state, uint64_t target, uint64_t br_ia);
|
||||
void (*XeInvalidInstruction)(
|
||||
xe_ppc_state_t* state, uint32_t cia, uint32_t data);
|
||||
void (*XeAccessViolation)(
|
||||
xe_ppc_state_t* state, uint32_t cia, uint64_t ea);
|
||||
void (*XeTraceKernelCall)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
kernel::KernelExport* kernel_export);
|
||||
void (*XeTraceUserCall)(
|
||||
xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
sdb::FunctionSymbol* fn);
|
||||
void (*XeTraceInstruction)(
|
||||
xe_ppc_state_t* state, uint32_t cia, uint32_t data);
|
||||
} GlobalExports;
|
||||
|
||||
|
||||
void GetGlobalExports(GlobalExports* global_exports);
|
||||
|
||||
|
||||
} // cpu
|
||||
} // xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_GLOBAL_EXPORTS_H_
|
|
@ -0,0 +1,55 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_JIT_H_
|
||||
#define XENIA_CPU_JIT_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/function_table.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class ExecModule;
|
||||
|
||||
|
||||
class JIT {
|
||||
public:
|
||||
virtual ~JIT() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
virtual int Setup() = 0;
|
||||
|
||||
virtual int InitModule(ExecModule* module) = 0;
|
||||
virtual int UninitModule(ExecModule* module) = 0;
|
||||
|
||||
virtual FunctionPointer GenerateFunction(sdb::FunctionSymbol* symbol) = 0;
|
||||
|
||||
protected:
|
||||
JIT(xe_memory_ref memory, FunctionTable* fn_table) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
fn_table_ = fn_table;
|
||||
}
|
||||
|
||||
xe_memory_ref memory_;
|
||||
FunctionTable* fn_table_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_JIT_H_
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LIBRARY_LINKER_H_
|
||||
#define XENIA_CPU_LIBRARY_LINKER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class LibraryLinker {
|
||||
public:
|
||||
virtual ~LibraryLinker() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
protected:
|
||||
LibraryLinker(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
}
|
||||
|
||||
xe_memory_ref memory_;
|
||||
kernel::ExportResolver* export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LIBRARY_LINKER_H_
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LIBRARY_LOADER_H_
|
||||
#define XENIA_CPU_LIBRARY_LOADER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
|
||||
class LibraryLoader {
|
||||
public:
|
||||
virtual ~LibraryLoader() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
protected:
|
||||
LibraryLoader(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
}
|
||||
|
||||
xe_memory_ref memory_;
|
||||
kernel::ExportResolver* export_resolver_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LIBRARY_LOADER_H_
|
|
@ -7,15 +7,15 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_EMIT_H_
|
||||
#define XENIA_CPU_CODEGEN_EMIT_H_
|
||||
#ifndef XENIA_CPU_LLVMBE_EMIT_H_
|
||||
#define XENIA_CPU_LLVMBE_EMIT_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
void RegisterEmitCategoryALU();
|
||||
|
@ -33,9 +33,9 @@ void RegisterEmitCategoryMemory();
|
|||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_EMIT_H_
|
||||
#endif // XENIA_CPU_LLVMBE_EMIT_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -7,25 +7,25 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
#include <xenia/cpu/llvmbe/emit.h>
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
int XeEmitIndirectBranchTo(
|
||||
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk, uint32_t reg) {
|
||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
||||
// a normal function return that is pulling the LR from the stack that
|
||||
|
@ -37,10 +37,10 @@ int XeEmitIndirectBranchTo(
|
|||
Value* target;
|
||||
switch (reg) {
|
||||
case kXEPPCRegLR:
|
||||
target = g.lr_value();
|
||||
target = e.lr_value();
|
||||
break;
|
||||
case kXEPPCRegCTR:
|
||||
target = g.ctr_value();
|
||||
target = e.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
|
@ -52,29 +52,29 @@ int XeEmitIndirectBranchTo(
|
|||
// Ideally it's a return and we can just do a simple ret and be done.
|
||||
// If it's not, we fall through to the full indirection logic.
|
||||
if (!lk && reg == kXEPPCRegLR) {
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* mismatch_bb = BasicBlock::Create(*g.context(), "lr_mismatch",
|
||||
g.gen_fn(), next_block);
|
||||
Value* lr_cmp = b.CreateICmpEQ(target, ++(g.gen_fn()->arg_begin()));
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
BasicBlock* mismatch_bb = BasicBlock::Create(*e.context(), "lr_mismatch",
|
||||
e.gen_fn(), next_block);
|
||||
Value* lr_cmp = b.CreateICmpEQ(target, ++(e.gen_fn()->arg_begin()));
|
||||
// The return block will spill registers for us.
|
||||
b.CreateCondBr(lr_cmp, g.GetReturnBasicBlock(), mismatch_bb);
|
||||
b.CreateCondBr(lr_cmp, e.GetReturnBasicBlock(), mismatch_bb);
|
||||
b.SetInsertPoint(mismatch_bb);
|
||||
}
|
||||
|
||||
// Defer to the generator, which will do fancy things.
|
||||
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
||||
return g.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||
return e.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||
}
|
||||
|
||||
int XeEmitBranchTo(
|
||||
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk) {
|
||||
// Get the basic block and switch behavior based on outgoing type.
|
||||
FunctionBlock* fn_block = g.fn_block();
|
||||
FunctionBlock* fn_block = e.fn_block();
|
||||
switch (fn_block->outgoing_type) {
|
||||
case FunctionBlock::kTargetBlock:
|
||||
{
|
||||
BasicBlock* target_bb = g.GetBasicBlock(fn_block->outgoing_address);
|
||||
BasicBlock* target_bb = e.GetBasicBlock(fn_block->outgoing_address);
|
||||
XEASSERTNOTNULL(target_bb);
|
||||
b.CreateBr(target_bb);
|
||||
break;
|
||||
|
@ -85,13 +85,13 @@ int XeEmitBranchTo(
|
|||
// TODO(benvanik): only spill ones used by the target function? Use
|
||||
// calling convention flags on the function to not spill temp
|
||||
// registers?
|
||||
g.SpillRegisters();
|
||||
e.SpillRegisters();
|
||||
|
||||
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
||||
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
||||
Function* target_fn = e.GetFunction(fn_block->outgoing_function);
|
||||
Function::arg_iterator args = e.gen_fn()->arg_begin();
|
||||
Value* state_ptr = args;
|
||||
BasicBlock* next_bb = g.GetNextBasicBlock();
|
||||
BasicBlock* next_bb = e.GetNextBasicBlock();
|
||||
if (!lk || !next_bb) {
|
||||
// Tail. No need to refill the local register values, just return.
|
||||
// We optimize this by passing in the LR from our parent instead of the
|
||||
|
@ -103,7 +103,7 @@ int XeEmitBranchTo(
|
|||
// Will return here eventually.
|
||||
// Refill registers from state.
|
||||
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
||||
g.FillRegisters();
|
||||
e.FillRegisters();
|
||||
b.CreateBr(next_bb);
|
||||
}
|
||||
break;
|
||||
|
@ -112,13 +112,13 @@ int XeEmitBranchTo(
|
|||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegLR);
|
||||
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegLR);
|
||||
}
|
||||
case FunctionBlock::kTargetCTR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegCTR);
|
||||
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegCTR);
|
||||
}
|
||||
default:
|
||||
case FunctionBlock::kTargetNone:
|
||||
|
@ -129,7 +129,7 @@ int XeEmitBranchTo(
|
|||
}
|
||||
|
||||
|
||||
XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(bx, 0x48000000, I )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if AA then
|
||||
// NIA <- EXTS(LI || 0b00)
|
||||
// else
|
||||
|
@ -144,13 +144,13 @@ XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
nia = i.address + XEEXTS26(i.I.LI << 2);
|
||||
}
|
||||
if (i.I.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
return XeEmitBranchTo(g, b, "bx", i.address, i.I.LK);
|
||||
return XeEmitBranchTo(e, b, "bx", i.address, i.I.LK);
|
||||
}
|
||||
|
||||
XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(bcx, 0x40000000, B )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
|
||||
|
@ -170,7 +170,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.B.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
|
@ -178,9 +178,9 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = g.ctr_value();
|
||||
Value* ctr = e.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
g.update_ctr_value(ctr);
|
||||
e.update_ctr_value(ctr);
|
||||
|
||||
// Ctr check.
|
||||
if (XESELECTBITS(i.B.BO, 1, 1)) {
|
||||
|
@ -194,7 +194,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
if (XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.B.BI >> 2);
|
||||
Value* cr = e.cr_value(i.B.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
|
||||
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
|
@ -218,8 +218,8 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
|
@ -233,7 +233,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
} else {
|
||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
||||
}
|
||||
if (XeEmitBranchTo(g, b, "bcx", i.address, i.B.LK)) {
|
||||
if (XeEmitBranchTo(e, b, "bcx", i.address, i.B.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -242,7 +242,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if cond_ok then
|
||||
// NIA <- CTR[0:61] || 0b00
|
||||
|
@ -256,14 +256,14 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||
Value* cr = e.cr_value(i.XL.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
|
@ -283,9 +283,9 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
|
@ -293,7 +293,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(g, b, "bcctrx", i.address, i.XL.LK)) {
|
||||
if (XeEmitBranchTo(e, b, "bcctrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -302,7 +302,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(bclrx, 0x4C000020, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
|
||||
|
@ -319,7 +319,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
g.update_lr_value(b.getInt32(i.address + 4));
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
|
@ -327,7 +327,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = g.ctr_value();
|
||||
Value* ctr = e.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
|
||||
// Ctr check.
|
||||
|
@ -342,7 +342,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = g.cr_value(i.XL.BI >> 2);
|
||||
Value* cr = e.cr_value(i.XL.BI >> 2);
|
||||
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||
|
@ -366,9 +366,9 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
|
||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
|
@ -376,7 +376,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(g, b, "bclrx", i.address, i.XL.LK)) {
|
||||
if (XeEmitBranchTo(e, b, "bclrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -388,47 +388,47 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEEMITTER(crand, 0x4C000202, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crand, 0x4C000202, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crandc, 0x4C000102, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crandc, 0x4C000102, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(creqv, 0x4C000242, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(creqv, 0x4C000242, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnor, 0x4C000042, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crnor, 0x4C000042, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(cror, 0x4C000382, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(cror, 0x4C000382, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crorc, 0x4C000342, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crorc, 0x4C000342, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crxor, 0x4C000182, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(crxor, 0x4C000182, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mcrf, 0x4C000000, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -436,7 +436,7 @@ XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(sc, 0x44000002, SC )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -444,7 +444,7 @@ XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Trap (A-25)
|
||||
|
||||
int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
||||
int XeEmitTrap(EmitterContext& e, IRBuilder<>& b, InstrData& i,
|
||||
Value* va, Value* vb, uint32_t TO) {
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
|
@ -459,27 +459,27 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
|||
return 0;
|
||||
}
|
||||
|
||||
BasicBlock* after_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
||||
g.GetNextBasicBlock());
|
||||
BasicBlock* trap_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
||||
BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||
e.GetNextBasicBlock());
|
||||
BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||
after_bb);
|
||||
|
||||
// Create the basic blocks (so we can chain).
|
||||
std::vector<BasicBlock*> bbs;
|
||||
if (TO & (1 << 4)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
bbs.push_back(after_bb);
|
||||
|
||||
|
@ -526,10 +526,10 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
|||
|
||||
// Create trap BB.
|
||||
b.SetInsertPoint(trap_bb);
|
||||
g.SpillRegisters();
|
||||
e.SpillRegisters();
|
||||
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
||||
b.CreateCall2(g.gen_module()->getFunction("XeTrap"),
|
||||
g.gen_fn()->arg_begin(),
|
||||
b.CreateCall2(e.gen_module()->getFunction("XeTrap"),
|
||||
e.gen_fn()->arg_begin(),
|
||||
b.getInt32(i.address));
|
||||
b.CreateBr(after_bb);
|
||||
|
||||
|
@ -539,7 +539,7 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
|||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(td, 0x7C000088, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
|
@ -547,26 +547,26 @@ XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
g.gpr_value(i.X.RA),
|
||||
g.gpr_value(i.X.RB),
|
||||
return XeEmitTrap(e, b, i,
|
||||
e.gpr_value(i.X.RA),
|
||||
e.gpr_value(i.X.RB),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tdi, 0x08000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(tdi, 0x08000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
g.gpr_value(i.D.RA),
|
||||
return XeEmitTrap(e, b, i,
|
||||
e.gpr_value(i.D.RA),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
i.D.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(tw, 0x7C000008, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// b <- EXTS((RB)[32:63])
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
|
@ -574,25 +574,25 @@ XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RA),
|
||||
return XeEmitTrap(e, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RB),
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RB),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(twi, 0x0C000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(g, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.D.RA),
|
||||
return XeEmitTrap(e, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.D.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
|
@ -602,12 +602,12 @@ XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEEMITTER(mfcr, 0x7C000026, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mfcr, 0x7C000026, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// RT <- SPR(n)
|
||||
|
@ -619,58 +619,58 @@ XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = g.xer_value();
|
||||
v = e.xer_value();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = g.lr_value();
|
||||
v = e.lr_value();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = g.ctr_value();
|
||||
v = e.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
g.update_gpr_value(i.XFX.RT, v);
|
||||
e.update_gpr_value(i.XFX.RT, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// SPR(n) <- (RS)
|
||||
// else
|
||||
// SPR(n) <- (RS)[32:63]
|
||||
|
||||
Value* v = g.gpr_value(i.XFX.RT);
|
||||
Value* v = e.gpr_value(i.XFX.RT);
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
g.update_xer_value(v);
|
||||
e.update_xer_value(v);
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
g.update_lr_value(v);
|
||||
e.update_lr_value(v);
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
g.update_ctr_value(v);
|
||||
e.update_ctr_value(v);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
|
@ -708,6 +708,6 @@ void RegisterEmitCategoryControl() {
|
|||
}
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
|
@ -7,84 +7,84 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
#include <xenia/cpu/llvmbe/emit.h>
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEEMITTER(faddx, 0xFC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(faddx, 0xFC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(faddsx, 0xEC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(faddsx, 0xEC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivx, 0xFC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fdivx, 0xFC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivsx, 0xEC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fdivsx, 0xEC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulx, 0xFC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmulx, 0xFC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulsx, 0xEC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmulsx, 0xEC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fresx, 0xEC000030, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fresx, 0xEC000030, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubx, 0xFC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fsubx, 0xFC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubsx, 0xEC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fsubsx, 0xEC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fselx, 0xFC00002E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fselx, 0xFC00002E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -92,42 +92,42 @@ XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubx, 0xFC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmsubx, 0xFC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -135,32 +135,32 @@ XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidx, 0xFC00065C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fctidx, 0xFC00065C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(frspx, 0xFC000018, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -168,12 +168,12 @@ XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
XEEMITTER(fcmpo, 0xFC000040, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fcmpo, 0xFC000040, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fcmpu, 0xFC000000, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if (FRA) is a NaN or (FRB) is a NaN then
|
||||
// c <- 0b0001
|
||||
// else if (FRA) < (FRB) then
|
||||
|
@ -194,32 +194,32 @@ XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEEMITTER(mcrfs, 0xFC000080, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mcrfs, 0xFC000080, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mffsx, 0xFC00048E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mffsx, 0xFC00048E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -227,22 +227,22 @@ XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
|||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEEMITTER(fabsx, 0xFC000210, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fabsx, 0xFC000210, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmrx, 0xFC000090, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fmrx, 0xFC000090, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnabsx, 0xFC000110, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnabsx, 0xFC000110, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnegx, 0xFC000050, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||
XEEMITTER(fnegx, 0xFC000050, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
@ -291,6 +291,6 @@ void RegisterEmitCategoryFPU() {
|
|||
}
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
File diff suppressed because it is too large
Load Diff
|
@ -7,7 +7,7 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
|
||||
#include <llvm/IR/Intrinsics.h>
|
||||
|
||||
|
@ -16,7 +16,7 @@
|
|||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
@ -29,8 +29,9 @@ DEFINE_bool(log_codegen, false,
|
|||
|
||||
/**
|
||||
* This generates function code.
|
||||
* One context is created for each function to generate. Each basic block in
|
||||
* the function is created and stashed in one pass, then filled in the next.
|
||||
* One context is created and shared for each function to generate.
|
||||
* Each basic block in the function is created and stashed in one pass, then
|
||||
* filled in the next.
|
||||
*
|
||||
* This context object is a stateful representation of the current machine state
|
||||
* and all accessors to registers should occur through it. By doing so it's
|
||||
|
@ -46,22 +47,44 @@ DEFINE_bool(log_codegen, false,
|
|||
*/
|
||||
|
||||
|
||||
FunctionGenerator::FunctionGenerator(
|
||||
xe_memory_ref memory, SymbolDatabase* sdb, FunctionSymbol* fn,
|
||||
LLVMContext* context, Module* gen_module, Function* gen_fn) {
|
||||
EmitterContext::EmitterContext(
|
||||
xe_memory_ref memory,
|
||||
LLVMContext* context, Module* gen_module) {
|
||||
memory_ = memory;
|
||||
sdb_ = sdb;
|
||||
fn_ = fn;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
gen_fn_ = gen_fn;
|
||||
builder_ = new IRBuilder<>(*context_);
|
||||
|
||||
// Function type for all functions.
|
||||
std::vector<Type*> args;
|
||||
args.push_back(PointerType::getUnqual(Type::getInt8Ty(*context)));
|
||||
args.push_back(Type::getInt64Ty(*context));
|
||||
Type* return_type = Type::getVoidTy(*context);
|
||||
fn_type_ = FunctionType::get(
|
||||
return_type, ArrayRef<Type*>(args), false);
|
||||
}
|
||||
|
||||
EmitterContext::~EmitterContext() {
|
||||
delete builder_;
|
||||
}
|
||||
|
||||
int EmitterContext::Init(FunctionSymbol* fn, Function* gen_fn) {
|
||||
builder_->ClearInsertionPoint();
|
||||
|
||||
fn_ = fn;
|
||||
gen_fn_ = gen_fn;
|
||||
|
||||
fn_block_ = NULL;
|
||||
return_block_ = NULL;
|
||||
internal_indirection_block_ = NULL;
|
||||
external_indirection_block_ = NULL;
|
||||
bb_ = NULL;
|
||||
|
||||
insert_points_.clear();
|
||||
bbs_.clear();
|
||||
|
||||
cia_ = 0;
|
||||
|
||||
access_bits_.Clear();
|
||||
|
||||
locals_.indirection_target = NULL;
|
||||
|
@ -80,53 +103,49 @@ FunctionGenerator::FunctionGenerator(
|
|||
locals_.fpr[n] = NULL;
|
||||
}
|
||||
|
||||
if (FLAGS_log_codegen) {
|
||||
printf("%s:\n", fn->name());
|
||||
if (fn) {
|
||||
if (FLAGS_log_codegen) {
|
||||
printf("%s:\n", fn->name());
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionGenerator::~FunctionGenerator() {
|
||||
delete builder_;
|
||||
}
|
||||
|
||||
SymbolDatabase* FunctionGenerator::sdb() {
|
||||
return sdb_;
|
||||
}
|
||||
|
||||
FunctionSymbol* FunctionGenerator::fn() {
|
||||
return fn_;
|
||||
}
|
||||
|
||||
llvm::LLVMContext* FunctionGenerator::context() {
|
||||
llvm::LLVMContext* EmitterContext::context() {
|
||||
return context_;
|
||||
}
|
||||
|
||||
llvm::Module* FunctionGenerator::gen_module() {
|
||||
llvm::Module* EmitterContext::gen_module() {
|
||||
return gen_module_;
|
||||
}
|
||||
|
||||
llvm::Function* FunctionGenerator::gen_fn() {
|
||||
FunctionSymbol* EmitterContext::fn() {
|
||||
return fn_;
|
||||
}
|
||||
|
||||
llvm::Function* EmitterContext::gen_fn() {
|
||||
return gen_fn_;
|
||||
}
|
||||
|
||||
FunctionBlock* FunctionGenerator::fn_block() {
|
||||
FunctionBlock* EmitterContext::fn_block() {
|
||||
return fn_block_;
|
||||
}
|
||||
|
||||
void FunctionGenerator::PushInsertPoint() {
|
||||
void EmitterContext::PushInsertPoint() {
|
||||
IRBuilder<>& b = *builder_;
|
||||
insert_points_.push_back(std::pair<BasicBlock*, BasicBlock::iterator>(
|
||||
b.GetInsertBlock(), b.GetInsertPoint()));
|
||||
}
|
||||
|
||||
void FunctionGenerator::PopInsertPoint() {
|
||||
void EmitterContext::PopInsertPoint() {
|
||||
IRBuilder<>& b = *builder_;
|
||||
std::pair<BasicBlock*, BasicBlock::iterator> back = insert_points_.back();
|
||||
b.SetInsertPoint(back.first, back.second);
|
||||
insert_points_.pop_back();
|
||||
}
|
||||
|
||||
void FunctionGenerator::GenerateBasicBlocks() {
|
||||
void EmitterContext::GenerateBasicBlocks() {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
// Always add an entry block.
|
||||
|
@ -178,7 +197,7 @@ void FunctionGenerator::GenerateBasicBlocks() {
|
|||
GenerateSharedBlocks();
|
||||
}
|
||||
|
||||
void FunctionGenerator::GenerateSharedBlocks() {
|
||||
void EmitterContext::GenerateSharedBlocks() {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
Value* indirect_branch = gen_module_->getFunction("XeIndirectBranch");
|
||||
|
@ -225,7 +244,7 @@ void FunctionGenerator::GenerateSharedBlocks() {
|
|||
}
|
||||
}
|
||||
|
||||
int FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
|
||||
int EmitterContext::PrepareBasicBlock(FunctionBlock* block) {
|
||||
// Create the basic block that will end up getting filled during
|
||||
// generation.
|
||||
char name[32];
|
||||
|
@ -271,7 +290,7 @@ int FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
||||
void EmitterContext::GenerateBasicBlock(FunctionBlock* block) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
BasicBlock* bb = GetBasicBlock(block->start_address);
|
||||
|
@ -337,7 +356,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
|||
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
||||
// ia >> 8, ia & 0xFF, ctx->cu));
|
||||
|
||||
typedef int (*InstrEmitter)(FunctionGenerator& g, IRBuilder<>& b,
|
||||
typedef int (*InstrEmitter)(EmitterContext& g, IRBuilder<>& b,
|
||||
InstrData& i);
|
||||
InstrEmitter emit = (InstrEmitter)i.type->emit;
|
||||
if (!i.type->emit || emit(*this, *builder_, i)) {
|
||||
|
@ -371,7 +390,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
|||
// TODO(benvanik): finish up BB
|
||||
}
|
||||
|
||||
BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
|
||||
BasicBlock* EmitterContext::GetBasicBlock(uint32_t address) {
|
||||
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(address);
|
||||
if (it != bbs_.end()) {
|
||||
return it->second;
|
||||
|
@ -379,7 +398,7 @@ BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
BasicBlock* FunctionGenerator::GetNextBasicBlock() {
|
||||
BasicBlock* EmitterContext::GetNextBasicBlock() {
|
||||
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(
|
||||
fn_block_->start_address);
|
||||
++it;
|
||||
|
@ -389,22 +408,49 @@ BasicBlock* FunctionGenerator::GetNextBasicBlock() {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
BasicBlock* FunctionGenerator::GetReturnBasicBlock() {
|
||||
BasicBlock* EmitterContext::GetReturnBasicBlock() {
|
||||
return return_block_;
|
||||
}
|
||||
|
||||
Function* FunctionGenerator::GetFunction(FunctionSymbol* fn) {
|
||||
Function* result = gen_module_->getFunction(StringRef(fn->name()));
|
||||
if (!result) {
|
||||
XELOGE("Static function not found: %.8X %s",
|
||||
fn->start_address, fn->name());
|
||||
Function* EmitterContext::GetFunction(FunctionSymbol* symbol) {
|
||||
StringRef fn_name(symbol->name());
|
||||
Function* fn = gen_module_->getFunction(fn_name);
|
||||
if (fn) {
|
||||
return fn;
|
||||
}
|
||||
XEASSERTNOTNULL(result);
|
||||
return result;
|
||||
|
||||
fn = cast<Function>(gen_module_->getOrInsertFunction(fn_name, fn_type_));
|
||||
fn->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
// Indicate that the function will never be unwound with an exception.
|
||||
// If we ever support native exception handling we may need to remove this.
|
||||
fn->doesNotThrow();
|
||||
|
||||
// May be worth trying the X86_FastCall, as we only need state in a register.
|
||||
//f->setCallingConv(CallingConv::Fast);
|
||||
fn->setCallingConv(CallingConv::C);
|
||||
|
||||
Function::arg_iterator fn_args = fn->arg_begin();
|
||||
|
||||
// 'state'
|
||||
Value* fn_arg = fn_args++;
|
||||
fn_arg->setName("state");
|
||||
fn->setDoesNotAlias(1);
|
||||
fn->setDoesNotCapture(1);
|
||||
// 'state' should try to be in a register, if possible.
|
||||
// TODO(benvanik): verify that's a good idea.
|
||||
// fn->getArgumentList().begin()->addAttr(
|
||||
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
|
||||
|
||||
// 'lr'
|
||||
fn_arg = fn_args++;
|
||||
fn_arg->setName("lr");
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
||||
bool lk, bool likely_local) {
|
||||
int EmitterContext::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
||||
bool lk, bool likely_local) {
|
||||
// This function is called by the control emitters when they know that an
|
||||
// indirect branch is required.
|
||||
// It first tries to see if the branch is to an address within the function
|
||||
|
@ -489,8 +535,8 @@ int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
|||
return 0;
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
|
||||
const char* name) {
|
||||
Value* EmitterContext::LoadStateValue(uint32_t offset, Type* type,
|
||||
const char* name) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
PointerType* pointerTy = PointerType::getUnqual(type);
|
||||
Function::arg_iterator args = gen_fn_->arg_begin();
|
||||
|
@ -500,8 +546,8 @@ Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
|
|||
return b.CreateLoad(ptr, name);
|
||||
}
|
||||
|
||||
void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
|
||||
Value* value) {
|
||||
void EmitterContext::StoreStateValue(uint32_t offset, Type* type,
|
||||
Value* value) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
PointerType* pointerTy = PointerType::getUnqual(type);
|
||||
Function::arg_iterator args = gen_fn_->arg_begin();
|
||||
|
@ -511,7 +557,7 @@ void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
|
|||
b.CreateStore(value, ptr);
|
||||
}
|
||||
|
||||
void FunctionGenerator::SetupLocals() {
|
||||
void EmitterContext::SetupLocals() {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
uint64_t spr_t = access_bits_.spr;
|
||||
|
@ -559,7 +605,7 @@ void FunctionGenerator::SetupLocals() {
|
|||
}
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::SetupLocal(llvm::Type* type, const char* name) {
|
||||
Value* EmitterContext::SetupLocal(llvm::Type* type, const char* name) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
// Insert into the entry block.
|
||||
PushInsertPoint();
|
||||
|
@ -569,11 +615,11 @@ Value* FunctionGenerator::SetupLocal(llvm::Type* type, const char* name) {
|
|||
return v;
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::cia_value() {
|
||||
Value* EmitterContext::cia_value() {
|
||||
return builder_->getInt32(cia_);
|
||||
}
|
||||
|
||||
void FunctionGenerator::FillRegisters() {
|
||||
void EmitterContext::FillRegisters() {
|
||||
// This updates all of the local register values from the state memory.
|
||||
// It should be called on function entry for initial setup and after any
|
||||
// calls that may modify the registers.
|
||||
|
@ -637,7 +683,7 @@ void FunctionGenerator::FillRegisters() {
|
|||
}
|
||||
}
|
||||
|
||||
void FunctionGenerator::SpillRegisters() {
|
||||
void EmitterContext::SpillRegisters() {
|
||||
// This flushes all local registers (if written) to the register bank and
|
||||
// resets their values.
|
||||
//
|
||||
|
@ -709,13 +755,13 @@ void FunctionGenerator::SpillRegisters() {
|
|||
}
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::xer_value() {
|
||||
Value* EmitterContext::xer_value() {
|
||||
XEASSERTNOTNULL(locals_.xer);
|
||||
IRBuilder<>& b = *builder_;
|
||||
return b.CreateLoad(locals_.xer);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_xer_value(Value* value) {
|
||||
void EmitterContext::update_xer_value(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.xer);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -726,7 +772,7 @@ void FunctionGenerator::update_xer_value(Value* value) {
|
|||
b.CreateStore(value, locals_.xer);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_xer_with_overflow(Value* value) {
|
||||
void EmitterContext::update_xer_with_overflow(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.xer);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -743,7 +789,7 @@ void FunctionGenerator::update_xer_with_overflow(Value* value) {
|
|||
b.CreateStore(xer, locals_.xer);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_xer_with_carry(Value* value) {
|
||||
void EmitterContext::update_xer_with_carry(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.xer);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -759,7 +805,7 @@ void FunctionGenerator::update_xer_with_carry(Value* value) {
|
|||
b.CreateStore(xer, locals_.xer);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_xer_with_overflow_and_carry(Value* value) {
|
||||
void EmitterContext::update_xer_with_overflow_and_carry(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.xer);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -779,13 +825,13 @@ void FunctionGenerator::update_xer_with_overflow_and_carry(Value* value) {
|
|||
b.CreateStore(xer, locals_.xer);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::lr_value() {
|
||||
Value* EmitterContext::lr_value() {
|
||||
XEASSERTNOTNULL(locals_.lr);
|
||||
IRBuilder<>& b = *builder_;
|
||||
return b.CreateLoad(locals_.lr);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_lr_value(Value* value) {
|
||||
void EmitterContext::update_lr_value(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.lr);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -796,14 +842,14 @@ void FunctionGenerator::update_lr_value(Value* value) {
|
|||
b.CreateStore(value, locals_.lr);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::ctr_value() {
|
||||
Value* EmitterContext::ctr_value() {
|
||||
XEASSERTNOTNULL(locals_.ctr);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
return b.CreateLoad(locals_.ctr);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_ctr_value(Value* value) {
|
||||
void EmitterContext::update_ctr_value(Value* value) {
|
||||
XEASSERTNOTNULL(locals_.ctr);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -814,7 +860,7 @@ void FunctionGenerator::update_ctr_value(Value* value) {
|
|||
b.CreateStore(value, locals_.ctr);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::cr_value(uint32_t n) {
|
||||
Value* EmitterContext::cr_value(uint32_t n) {
|
||||
XEASSERT(n >= 0 && n < 8);
|
||||
XEASSERTNOTNULL(locals_.cr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
@ -824,7 +870,7 @@ Value* FunctionGenerator::cr_value(uint32_t n) {
|
|||
return v;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
|
||||
void EmitterContext::update_cr_value(uint32_t n, Value* value) {
|
||||
XEASSERT(n >= 0 && n < 8);
|
||||
XEASSERTNOTNULL(locals_.cr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
@ -838,7 +884,7 @@ void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
|
|||
b.CreateStore(value, locals_.cr[n]);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_cr_with_cond(
|
||||
void EmitterContext::update_cr_with_cond(
|
||||
uint32_t n, Value* lhs, Value* rhs, bool is_signed) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -870,7 +916,7 @@ void FunctionGenerator::update_cr_with_cond(
|
|||
update_cr_value(n, c);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::gpr_value(uint32_t n) {
|
||||
Value* EmitterContext::gpr_value(uint32_t n) {
|
||||
XEASSERT(n >= 0 && n < 32);
|
||||
XEASSERTNOTNULL(locals_.gpr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
@ -885,7 +931,7 @@ Value* FunctionGenerator::gpr_value(uint32_t n) {
|
|||
return b.CreateLoad(locals_.gpr[n]);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
|
||||
void EmitterContext::update_gpr_value(uint32_t n, Value* value) {
|
||||
XEASSERT(n >= 0 && n < 32);
|
||||
XEASSERTNOTNULL(locals_.gpr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
@ -904,14 +950,14 @@ void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
|
|||
b.CreateStore(value, locals_.gpr[n]);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::fpr_value(uint32_t n) {
|
||||
Value* EmitterContext::fpr_value(uint32_t n) {
|
||||
XEASSERT(n >= 0 && n < 32);
|
||||
XEASSERTNOTNULL(locals_.fpr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
return b.CreateLoad(locals_.fpr[n]);
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_fpr_value(uint32_t n, Value* value) {
|
||||
void EmitterContext::update_fpr_value(uint32_t n, Value* value) {
|
||||
XEASSERT(n >= 0 && n < 32);
|
||||
XEASSERTNOTNULL(locals_.fpr[n]);
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
@ -919,12 +965,12 @@ void FunctionGenerator::update_fpr_value(uint32_t n, Value* value) {
|
|||
b.CreateStore(value, locals_.fpr[n]);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::GetMembase() {
|
||||
Value* EmitterContext::GetMembase() {
|
||||
Value* v = gen_module_->getGlobalVariable("xe_memory_base");
|
||||
return builder_->CreateLoad(v);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::GetMemoryAddress(uint32_t cia, Value* addr) {
|
||||
Value* EmitterContext::GetMemoryAddress(uint32_t cia, Value* addr) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
// Input address is always in 32-bit space.
|
||||
|
@ -955,7 +1001,7 @@ Value* FunctionGenerator::GetMemoryAddress(uint32_t cia, Value* addr) {
|
|||
return b.CreateInBoundsGEP(GetMembase(), addr);
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::ReadMemory(
|
||||
Value* EmitterContext::ReadMemory(
|
||||
uint32_t cia, Value* addr, uint32_t size, bool acquire) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
||||
|
@ -1004,7 +1050,7 @@ Value* FunctionGenerator::ReadMemory(
|
|||
return value;
|
||||
}
|
||||
|
||||
void FunctionGenerator::WriteMemory(
|
||||
void EmitterContext::WriteMemory(
|
||||
uint32_t cia, Value* addr, uint32_t size, Value* value, bool release) {
|
||||
IRBuilder<>& b = *builder_;
|
||||
|
|
@ -7,8 +7,8 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
#define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
#ifndef XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
||||
#define XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
||||
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
|
@ -23,21 +23,22 @@
|
|||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace codegen {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class FunctionGenerator {
|
||||
class EmitterContext {
|
||||
public:
|
||||
FunctionGenerator(
|
||||
xe_memory_ref memory, sdb::SymbolDatabase* sdb, sdb::FunctionSymbol* fn,
|
||||
llvm::LLVMContext* context, llvm::Module* gen_module,
|
||||
llvm::Function* gen_fn);
|
||||
~FunctionGenerator();
|
||||
EmitterContext(
|
||||
xe_memory_ref memory,
|
||||
llvm::LLVMContext* context, llvm::Module* gen_module);
|
||||
~EmitterContext();
|
||||
|
||||
int Init(sdb::FunctionSymbol* fn, llvm::Function* gen_fn);
|
||||
|
||||
sdb::SymbolDatabase* sdb();
|
||||
sdb::FunctionSymbol* fn();
|
||||
llvm::LLVMContext* context();
|
||||
llvm::Module* gen_module();
|
||||
|
||||
sdb::FunctionSymbol* fn();
|
||||
llvm::Function* gen_fn();
|
||||
sdb::FunctionBlock* fn_block();
|
||||
|
||||
|
@ -49,7 +50,7 @@ public:
|
|||
llvm::BasicBlock* GetNextBasicBlock();
|
||||
llvm::BasicBlock* GetReturnBasicBlock();
|
||||
|
||||
llvm::Function* GetFunction(sdb::FunctionSymbol* fn);
|
||||
llvm::Function* GetFunction(sdb::FunctionSymbol* symbol);
|
||||
|
||||
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
|
||||
bool lk, bool likely_local);
|
||||
|
@ -101,17 +102,18 @@ private:
|
|||
void SetupLocals();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
sdb::SymbolDatabase* sdb_;
|
||||
sdb::FunctionSymbol* fn_;
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* gen_module_;
|
||||
llvm::IRBuilder<>* builder_;
|
||||
llvm::FunctionType* fn_type_;
|
||||
|
||||
sdb::FunctionSymbol* fn_;
|
||||
llvm::Function* gen_fn_;
|
||||
sdb::FunctionBlock* fn_block_;
|
||||
llvm::BasicBlock* return_block_;
|
||||
llvm::BasicBlock* internal_indirection_block_;
|
||||
llvm::BasicBlock* external_indirection_block_;
|
||||
llvm::BasicBlock* bb_;
|
||||
llvm::IRBuilder<>* builder_;
|
||||
|
||||
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
|
||||
insert_points_;
|
||||
|
@ -136,9 +138,9 @@ private:
|
|||
};
|
||||
|
||||
|
||||
} // namespace codegen
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
||||
#endif // XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_backend.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>
|
||||
|
||||
#include <xenia/cpu/llvmbe/emit.h>
|
||||
#include <xenia/cpu/llvmbe/llvm_jit.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
namespace {
|
||||
void InitializeIfNeeded();
|
||||
void CleanupOnShutdown();
|
||||
|
||||
void InitializeIfNeeded() {
|
||||
static bool has_initialized = false;
|
||||
if (has_initialized) {
|
||||
return;
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
|
||||
llvm_start_multithreaded();
|
||||
|
||||
llvmbe::RegisterEmitCategoryALU();
|
||||
llvmbe::RegisterEmitCategoryControl();
|
||||
llvmbe::RegisterEmitCategoryFPU();
|
||||
llvmbe::RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {
|
||||
llvm_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMBackend::LLVMBackend() :
|
||||
Backend() {
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
LLVMBackend::~LLVMBackend() {
|
||||
}
|
||||
|
||||
CodeUnitBuilder* LLVMBackend::CreateCodeUnitBuilder() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LibraryLinker* LLVMBackend::CreateLibraryLinker() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LibraryLoader* LLVMBackend::CreateLibraryLoader() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JIT* LLVMBackend::CreateJIT(xe_memory_ref memory, FunctionTable* fn_table) {
|
||||
return new LLVMJIT(memory, fn_table);
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_BACKEND_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_BACKEND_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/cpu/backend.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMBackend : public Backend {
|
||||
public:
|
||||
LLVMBackend();
|
||||
virtual ~LLVMBackend();
|
||||
|
||||
virtual CodeUnitBuilder* CreateCodeUnitBuilder();
|
||||
virtual LibraryLinker* CreateLibraryLinker();
|
||||
virtual LibraryLoader* CreateLibraryLoader();
|
||||
|
||||
virtual JIT* CreateJIT(xe_memory_ref memory, FunctionTable* fn_table);
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_BACKEND_H_
|
|
@ -0,0 +1,264 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_code_unit_builder.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/DebugInfo.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#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/cpu-private.h>
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
LLVMCodeUnitBuilder::LLVMCodeUnitBuilder(
|
||||
xe_memory_ref memory, LLVMContext* context) :
|
||||
CodeUnitBuilder(memory),
|
||||
context_(context) {
|
||||
}
|
||||
|
||||
LLVMCodeUnitBuilder::~LLVMCodeUnitBuilder() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
Module* LLVMCodeUnitBuilder::module() {
|
||||
return module_;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::Init(const char* module_name,
|
||||
const char* module_path) {
|
||||
Reset();
|
||||
|
||||
// Create LLVM module.
|
||||
module_ = new Module(module_name, *context_);
|
||||
|
||||
// Create the emitter used to generate functions.
|
||||
emitter_ = new EmitterContext(memory_, context_, module_);
|
||||
|
||||
// Setup optimization pass.
|
||||
fpm_ = new FunctionPassManager(module_);
|
||||
if (FLAGS_optimize_ir_functions) {
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(*fpm_);
|
||||
}
|
||||
// TODO(benvanik): disable verifier in release builds?
|
||||
fpm_->add(createVerifierPass());
|
||||
|
||||
// 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.
|
||||
char dir[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path));
|
||||
char* slash = xestrrchra(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
di_builder_ = new DIBuilder(*module_);
|
||||
di_builder_->createCompileUnit(
|
||||
dwarf::DW_LANG_C99, //0x8010,
|
||||
StringRef(module_name),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeFunction(FunctionSymbol* symbol) {
|
||||
return MakeFunction(symbol, NULL);
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeFunction(FunctionSymbol* symbol,
|
||||
Function** out_fn) {
|
||||
int result_code = 0;
|
||||
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* fn = emitter_->GetFunction(symbol);
|
||||
|
||||
// If already defined, ignore.
|
||||
if (!fn->isDeclaration()) {
|
||||
if (out_fn) {
|
||||
*out_fn = fn;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (symbol->type) {
|
||||
case FunctionSymbol::User:
|
||||
result_code = MakeUserFunction(symbol, fn);
|
||||
break;
|
||||
case FunctionSymbol::Kernel:
|
||||
if (symbol->kernel_export && symbol->kernel_export->is_implemented) {
|
||||
result_code = MakePresentImportFunction(symbol, fn);
|
||||
} else {
|
||||
result_code = MakeMissingImportFunction(symbol, fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
if (result_code) {
|
||||
return result_code;
|
||||
}
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
OptimizeFunction(fn);
|
||||
|
||||
// Add to map for processing later.
|
||||
functions_.insert(std::pair<uint32_t, Function*>(symbol->start_address, fn));
|
||||
|
||||
if (out_fn) {
|
||||
*out_fn = fn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeUserFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
// Setup emitter.
|
||||
emitter_->Init(symbol, fn);
|
||||
|
||||
// Emit.
|
||||
emitter_->GenerateBasicBlocks();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakePresentImportFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
LLVMContext& context = *context_;
|
||||
|
||||
// Pick names.
|
||||
// We have both the shim function pointer and the shim data pointer.
|
||||
char shim_name[256];
|
||||
xesnprintfa(shim_name, XECOUNT(shim_name),
|
||||
"__shim_%s", symbol->kernel_export->name);
|
||||
char shim_data_name[256];
|
||||
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
|
||||
"__shim_data_%s", symbol->kernel_export->name);
|
||||
|
||||
// Hardcoded to 64bits.
|
||||
Type* intPtrTy = IntegerType::get(context, 64);
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
// Declare shim function.
|
||||
std::vector<Type*> shimArgs;
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
FunctionType* shimTy = FunctionType::get(
|
||||
Type::getVoidTy(context), shimArgs, false);
|
||||
Function* shim = Function::Create(
|
||||
shimTy, Function::ExternalLinkage, shim_name, module_);
|
||||
|
||||
GlobalVariable* shim_data = new GlobalVariable(
|
||||
*module_, int8PtrTy, false, GlobalValue::ExternalLinkage, 0,
|
||||
shim_data_name);
|
||||
shim_data->setInitializer(ConstantExpr::getIntToPtr(
|
||||
ConstantInt::get(intPtrTy, 0), int8PtrTy));
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", fn);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = module_->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
fn->arg_begin(),
|
||||
b.getInt64(symbol->start_address),
|
||||
++fn->arg_begin(),
|
||||
b.getInt64((uint64_t)symbol->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateCall2(
|
||||
shim,
|
||||
fn->arg_begin(),
|
||||
b.CreateLoad(shim_data));
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeMissingImportFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
BasicBlock* block = BasicBlock::Create(*context_, "entry", fn);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = module_->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
fn->arg_begin(),
|
||||
b.getInt64(symbol->start_address),
|
||||
++fn->arg_begin(),
|
||||
b.getInt64((uint64_t)symbol->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LLVMCodeUnitBuilder::OptimizeFunction(Function* fn) {
|
||||
//fn->dump();
|
||||
fpm_->run(*fn);
|
||||
fn->dump();
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::Finalize() {
|
||||
// Finalize debug info.
|
||||
di_builder_->finalize();
|
||||
|
||||
// TODO(benvanik): run module optimizations, if enabled.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LLVMCodeUnitBuilder::Reset() {
|
||||
functions_.clear();
|
||||
|
||||
delete emitter_;
|
||||
emitter_ = NULL;
|
||||
delete di_builder_;
|
||||
di_builder_ = NULL;
|
||||
delete fpm_;
|
||||
fpm_ = NULL;
|
||||
delete module_;
|
||||
module_ = NULL;
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/code_unit_builder.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class DIBuilder;
|
||||
class Function;
|
||||
class FunctionPassManager;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
class MDNode;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class EmitterContext;
|
||||
|
||||
|
||||
class LLVMCodeUnitBuilder : public CodeUnitBuilder {
|
||||
public:
|
||||
LLVMCodeUnitBuilder(xe_memory_ref memory, llvm::LLVMContext* context);
|
||||
virtual ~LLVMCodeUnitBuilder();
|
||||
|
||||
llvm::Module* module();
|
||||
|
||||
virtual int Init(const char* module_name, const char* module_path);
|
||||
virtual int MakeFunction(sdb::FunctionSymbol* symbol);
|
||||
int MakeFunction(sdb::FunctionSymbol* symbol, llvm::Function** out_fn);
|
||||
virtual int Finalize();
|
||||
virtual void Reset();
|
||||
|
||||
private:
|
||||
llvm::Function* CreateFunctionDeclaration(const char* name);
|
||||
int MakeUserFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
int MakePresentImportFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
int MakeMissingImportFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
void OptimizeFunction(llvm::Function* f);
|
||||
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* module_;
|
||||
llvm::FunctionPassManager* fpm_;
|
||||
llvm::DIBuilder* di_builder_;
|
||||
llvm::MDNode* cu_;
|
||||
EmitterContext* emitter_;
|
||||
|
||||
std::map<uint32_t, llvm::Function*> functions_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
|
@ -7,7 +7,7 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/llvm_exports.h>
|
||||
#include <xenia/cpu/llvmbe/llvm_exports.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
|
@ -16,95 +16,15 @@
|
|||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
#include <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
void XeTrap(xe_ppc_state_t* state, uint32_t cia) {
|
||||
XELOGE("TRAP");
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeIndirectBranch(xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
|
||||
XELOGCPU("INDIRECT BRANCH %.8X -> %.8X",
|
||||
(uint32_t)br_ia, (uint32_t)target);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeInvalidInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrData i;
|
||||
i.address = cia;
|
||||
i.code = data;
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
|
||||
if (!i.type) {
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X ???",
|
||||
i.address, i.code);
|
||||
} else if (i.type->disassemble) {
|
||||
ppc::InstrDisasm d;
|
||||
i.type->disassemble(i, d);
|
||||
std::string disasm;
|
||||
d.Dump(disasm);
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, disasm.c_str());
|
||||
} else {
|
||||
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
|
||||
i.address, i.code, i.type->name);
|
||||
}
|
||||
}
|
||||
|
||||
void XeAccessViolation(xe_ppc_state_t* state, uint32_t cia, uint64_t ea) {
|
||||
XELOGE("INVALID ACCESS %.8X: tried to touch %.8X",
|
||||
cia, (uint32_t)ea);
|
||||
XEASSERTALWAYS();
|
||||
}
|
||||
|
||||
void XeTraceKernelCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
KernelExport* kernel_export) {
|
||||
XELOGCPU("TRACE: %.8X -> k.%.8X (%s)",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia,
|
||||
kernel_export ? kernel_export->name : "unknown");
|
||||
}
|
||||
|
||||
void XeTraceUserCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
|
||||
FunctionSymbol* fn) {
|
||||
XELOGCPU("TRACE: %.8X -> u.%.8X (%s)",
|
||||
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
|
||||
}
|
||||
|
||||
void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
|
||||
ppc::InstrType* type = ppc::GetInstrType(data);
|
||||
XELOGCPU("TRACE: %.8X %.8X %s %s",
|
||||
cia, data,
|
||||
type && type->emit ? " " : "X",
|
||||
type ? type->name : "<unknown>");
|
||||
|
||||
if (cia == 0x82014468) {
|
||||
printf("BREAKBREAKBREAK\n");
|
||||
}
|
||||
|
||||
// TODO(benvanik): better disassembly, printing of current register values/etc
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||
const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine) {
|
||||
void xe::cpu::llvmbe::SetupLlvmExports(
|
||||
GlobalExports* global_exports,
|
||||
Module* module, const DataLayout* dl, ExecutionEngine* engine) {
|
||||
LLVMContext& context = module->getContext();
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
|
@ -116,7 +36,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
|||
Type::getVoidTy(context), trapArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
trapTy, Function::ExternalLinkage, "XeTrap",
|
||||
module), (void*)&XeTrap);
|
||||
module), (void*)(global_exports->XeTrap));
|
||||
|
||||
std::vector<Type*> indirectBranchArgs;
|
||||
indirectBranchArgs.push_back(int8PtrTy);
|
||||
|
@ -126,7 +46,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
|||
Type::getVoidTy(context), indirectBranchArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
|
||||
module), (void*)&XeIndirectBranch);
|
||||
module), (void*)(global_exports->XeIndirectBranch));
|
||||
|
||||
// Debugging methods:
|
||||
std::vector<Type*> invalidInstructionArgs;
|
||||
|
@ -137,7 +57,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
|||
Type::getVoidTy(context), invalidInstructionArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
|
||||
module), (void*)&XeInvalidInstruction);
|
||||
module), (void*)(global_exports->XeInvalidInstruction));
|
||||
|
||||
std::vector<Type*> accessViolationArgs;
|
||||
accessViolationArgs.push_back(int8PtrTy);
|
||||
|
@ -147,7 +67,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
|||
Type::getVoidTy(context), accessViolationArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
|
||||
module), (void*)&XeAccessViolation);
|
||||
module), (void*)(global_exports->XeAccessViolation));
|
||||
|
||||
// Tracing methods:
|
||||
std::vector<Type*> traceCallArgs;
|
||||
|
@ -166,11 +86,11 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
|||
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
|
||||
module), (void*)&XeTraceKernelCall);
|
||||
module), (void*)(global_exports->XeTraceKernelCall));
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
|
||||
module), (void*)&XeTraceUserCall);
|
||||
module), (void*)(global_exports->XeTraceUserCall));
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
|
||||
module), (void*)&XeTraceInstruction);
|
||||
module), (void*)(global_exports->XeTraceInstruction));
|
||||
}
|
|
@ -7,16 +7,17 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_LLVM_EXPORTS_H_
|
||||
#define XENIA_CPU_LLVM_EXPORTS_H_
|
||||
#ifndef XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/global_exports.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
class DataLayout;
|
||||
}
|
||||
|
@ -24,15 +25,18 @@ namespace llvm {
|
|||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
void SetupLlvmExports(llvm::Module* module,
|
||||
const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine);
|
||||
void SetupLlvmExports(
|
||||
GlobalExports* global_exports,
|
||||
llvm::Module* module, const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine);
|
||||
|
||||
|
||||
} // llvmbe
|
||||
} // cpu
|
||||
} // xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVM_EXPORTS_H_
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
|
@ -0,0 +1,211 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_JIT_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_JIT_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/function_table.h>
|
||||
#include <xenia/cpu/jit.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMCodeUnitBuilder;
|
||||
|
||||
|
||||
class LLVMJIT : public JIT {
|
||||
public:
|
||||
LLVMJIT(xe_memory_ref memory, FunctionTable* fn_table);
|
||||
virtual ~LLVMJIT();
|
||||
|
||||
virtual int Setup();
|
||||
|
||||
virtual int InitModule(ExecModule* module);
|
||||
virtual int UninitModule(ExecModule* module);
|
||||
|
||||
virtual FunctionPointer GenerateFunction(sdb::FunctionSymbol* symbol);
|
||||
|
||||
protected:
|
||||
int InjectGlobals();
|
||||
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::ExecutionEngine* engine_;
|
||||
llvm::Module* module_;
|
||||
|
||||
LLVMCodeUnitBuilder* cub_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_JIT_H_
|
|
@ -7,14 +7,18 @@
|
|||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is shared between xethunk and the loader to pass structures
|
||||
* between the two. Since this file is compiled with the LLVM clang it cannot
|
||||
* include any other files.
|
||||
*/
|
||||
|
||||
#ifndef XENIA_CPU_XETHUNK_H_
|
||||
#define XENIA_CPU_XETHUNK_H_
|
||||
#include <xenia/cpu/llvmbe/llvm_library_linker.h>
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XETHUNK_H_
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
LLVMLibraryLinker::LLVMLibraryLinker(
|
||||
xe_memory_ref memory, kernel::ExportResolver* export_resolver) :
|
||||
LibraryLinker(memory, export_resolver) {
|
||||
}
|
||||
|
||||
LLVMLibraryLinker::~LLVMLibraryLinker() {
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/library_linker.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMLibraryLinker : public LibraryLinker {
|
||||
public:
|
||||
LLVMLibraryLinker(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver);
|
||||
virtual ~LLVMLibraryLinker();
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
|
@ -0,0 +1,24 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_library_loader.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
LLVMLibraryLoader::LLVMLibraryLoader(
|
||||
xe_memory_ref memory, kernel::ExportResolver* export_resolver) :
|
||||
LibraryLoader(memory, export_resolver) {
|
||||
}
|
||||
|
||||
LLVMLibraryLoader::~LLVMLibraryLoader() {
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/library_loader.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMLibraryLoader : public LibraryLoader {
|
||||
public:
|
||||
LLVMLibraryLoader(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver);
|
||||
virtual ~LLVMLibraryLoader();
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
|
@ -0,0 +1,24 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'emit.h',
|
||||
'emit_alu.cc',
|
||||
'emit_control.cc',
|
||||
'emit_fpu.cc',
|
||||
'emit_memory.cc',
|
||||
'emitter_context.cc',
|
||||
'emitter_context.h',
|
||||
'llvm_backend.cc',
|
||||
'llvm_backend.h',
|
||||
'llvm_code_unit_builder.cc',
|
||||
'llvm_code_unit_builder.h',
|
||||
'llvm_exports.cc',
|
||||
'llvm_exports.h',
|
||||
'llvm_jit.cc',
|
||||
'llvm_jit.h',
|
||||
'llvm_library_linker.cc',
|
||||
'llvm_library_linker.h',
|
||||
'llvm_library_loader.cc',
|
||||
'llvm_library_loader.h',
|
||||
],
|
||||
}
|
|
@ -717,22 +717,12 @@ XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
|
|||
// TODO
|
||||
return d.Finish();
|
||||
}
|
||||
XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
|
||||
// No-op for now.
|
||||
// TODO(benvanik): use @llvm.prefetch
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
|
||||
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
|
||||
// TODO
|
||||
return d.Finish();
|
||||
}
|
||||
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
|
||||
// No-op for now.
|
||||
// TODO(benvanik): use @llvm.prefetch
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEDISASMR(dcbz, 0x7C0007EC, X )(InstrData& i, InstrDisasm& d) {
|
||||
// or dcbz128 0x7C2007EC
|
||||
|
|
|
@ -9,19 +9,10 @@
|
|||
|
||||
#include <xenia/cpu/processor.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.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>
|
||||
|
||||
#include <xenia/cpu/codegen/emit.h>
|
||||
#include <xenia/cpu/jit.h>
|
||||
#include <xenia/cpu/ppc/disasm.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::kernel;
|
||||
|
@ -38,36 +29,23 @@ namespace {
|
|||
}
|
||||
has_initialized = true;
|
||||
|
||||
// TODO(benvanik): only do this once
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
|
||||
llvm_start_multithreaded();
|
||||
|
||||
ppc::RegisterDisasmCategoryALU();
|
||||
ppc::RegisterDisasmCategoryControl();
|
||||
ppc::RegisterDisasmCategoryFPU();
|
||||
ppc::RegisterDisasmCategoryMemory();
|
||||
|
||||
// TODO(benvanik): only do this once
|
||||
codegen::RegisterEmitCategoryALU();
|
||||
codegen::RegisterEmitCategoryControl();
|
||||
codegen::RegisterEmitCategoryFPU();
|
||||
codegen::RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {
|
||||
llvm_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
|
||||
pal_ = xe_pal_retain(pal);
|
||||
Processor::Processor(xe_memory_ref memory, shared_ptr<Backend> backend) :
|
||||
fn_table_(NULL), jit_(NULL) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
backend_ = backend;
|
||||
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
@ -76,55 +54,55 @@ Processor::~Processor() {
|
|||
// Cleanup all modules.
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
delete *it;
|
||||
ExecModule* exec_module = *it;
|
||||
if (jit_) {
|
||||
jit_->UninitModule(exec_module);
|
||||
}
|
||||
delete exec_module;
|
||||
}
|
||||
modules_.clear();
|
||||
|
||||
engine_.reset();
|
||||
delete jit_;
|
||||
delete fn_table_;
|
||||
|
||||
export_resolver_.reset();
|
||||
backend_.reset();
|
||||
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_);
|
||||
}
|
||||
|
||||
shared_ptr<ExportResolver> Processor::export_resolver() {
|
||||
return export_resolver_;
|
||||
}
|
||||
|
||||
void Processor::set_export_resolver(
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
export_resolver_ = export_resolver;
|
||||
}
|
||||
|
||||
int Processor::Setup() {
|
||||
XEASSERTNULL(engine_);
|
||||
XEASSERTNULL(jit_);
|
||||
|
||||
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
|
||||
Module* dummy_module = new Module("dummy", *dummy_context_.get());
|
||||
fn_table_ = new FunctionTable();
|
||||
|
||||
std::string error_message;
|
||||
|
||||
EngineBuilder builder(dummy_module);
|
||||
builder.setEngineKind(EngineKind::JIT);
|
||||
builder.setErrorStr(&error_message);
|
||||
builder.setOptLevel(CodeGenOpt::None);
|
||||
//builder.setOptLevel(CodeGenOpt::Aggressive);
|
||||
//builder.setTargetOptions();
|
||||
builder.setAllocateGVsWithCode(false);
|
||||
//builder.setUseMCJIT(true);
|
||||
|
||||
engine_ = shared_ptr<ExecutionEngine>(builder.create());
|
||||
if (!engine_) {
|
||||
jit_ = backend_->CreateJIT(memory_, fn_table_);
|
||||
if (jit_->Setup()) {
|
||||
XELOGE("Unable to create JIT");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
|
||||
ExecModule* exec_module = NULL;
|
||||
const xechar_t* name = xestrrchr(path, '/') + 1;
|
||||
|
||||
// TODO(benvanik): map file from filesystem
|
||||
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
||||
// TODO(benvanik): map file from filesystem API, not via platform API.
|
||||
xe_mmap_ref mmap = xe_mmap_open(kXEFileModeRead, path, 0, 0);
|
||||
if (!mmap) {
|
||||
return NULL;
|
||||
}
|
||||
|
@ -133,30 +111,29 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
|||
|
||||
int result_code = 1;
|
||||
|
||||
// Place the data into memory at the desired address.
|
||||
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
||||
xe_memory_get_length(memory_),
|
||||
addr, length));
|
||||
|
||||
// Prepare the module.
|
||||
char name_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||
char path_a[XE_MAX_PATH];
|
||||
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
|
||||
|
||||
// Prepare the module.
|
||||
// This will analyze it, generate code (if needed), and adds methods to
|
||||
// the function table.
|
||||
exec_module = new ExecModule(
|
||||
memory_, export_resolver, name_a, path_a, engine_);
|
||||
memory_, export_resolver_, fn_table_, name_a, path_a);
|
||||
XEEXPECTZERO(exec_module->PrepareRawBinary(
|
||||
start_address, start_address + (uint32_t)length));
|
||||
|
||||
if (exec_module->PrepareRawBinary(start_address,
|
||||
start_address + (uint32_t)length)) {
|
||||
delete exec_module;
|
||||
return 1;
|
||||
}
|
||||
// Initialize the module and prepare it for execution.
|
||||
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||
|
||||
exec_module->AddFunctionsToMap(all_fns_);
|
||||
modules_.push_back(exec_module);
|
||||
|
||||
exec_module->Dump();
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
if (result_code) {
|
||||
|
@ -166,22 +143,28 @@ XECLEANUP:
|
|||
return result_code;
|
||||
}
|
||||
|
||||
int Processor::PrepareModule(const char* name, const char* path,
|
||||
xe_xex2_ref xex,
|
||||
shared_ptr<ExportResolver> export_resolver) {
|
||||
int Processor::LoadXexModule(const char* name, const char* path,
|
||||
xe_xex2_ref xex) {
|
||||
int result_code = 1;
|
||||
|
||||
// Prepare the module.
|
||||
// This will analyze it, generate code (if needed), and adds methods to
|
||||
// the function table.
|
||||
ExecModule* exec_module = new ExecModule(
|
||||
memory_, export_resolver, name, path,
|
||||
engine_);
|
||||
memory_, export_resolver_, fn_table_, name, path);
|
||||
XEEXPECTZERO(exec_module->PrepareXexModule(xex));
|
||||
|
||||
if (exec_module->PrepareXex(xex)) {
|
||||
delete exec_module;
|
||||
return 1;
|
||||
}
|
||||
// Initialize the module and prepare it for execution.
|
||||
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||
|
||||
exec_module->AddFunctionsToMap(all_fns_);
|
||||
modules_.push_back(exec_module);
|
||||
|
||||
return 0;
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
if (result_code) {
|
||||
delete exec_module;
|
||||
}
|
||||
return result_code;
|
||||
}
|
||||
|
||||
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
|
||||
|
@ -200,14 +183,34 @@ void Processor::DeallocThread(ThreadState* thread_state) {
|
|||
delete thread_state;
|
||||
}
|
||||
|
||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||
// Find the function to execute.
|
||||
Function* f = GetFunction(address);
|
||||
if (!f) {
|
||||
XELOGCPU("Failed to find function %.8X to execute.", address);
|
||||
return 1;
|
||||
FunctionPointer Processor::GenerateFunction(uint32_t address) {
|
||||
// Search all modules for the function symbol.
|
||||
// Each module will see if the address is within its code range and if the
|
||||
// symbol is not found (likely) it will do analysis on it.
|
||||
// TODO(benvanik): make this more efficient. Could use a binary search or
|
||||
// something more clever.
|
||||
sdb::FunctionSymbol* fn_symbol = NULL;
|
||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||
it != modules_.end(); ++it) {
|
||||
fn_symbol = (*it)->FindFunctionSymbol(address);
|
||||
if (fn_symbol) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fn_symbol) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// JIT the function.
|
||||
FunctionPointer f = jit_->GenerateFunction(fn_symbol);
|
||||
if (!f) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
||||
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||
|
||||
// This could be set to anything to give us a unique identifier to track
|
||||
|
@ -217,22 +220,23 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
|||
// Setup registers.
|
||||
ppc_state->lr = lr;
|
||||
|
||||
// Args:
|
||||
// - i8* state
|
||||
// - i64 lr
|
||||
std::vector<GenericValue> args;
|
||||
args.push_back(PTOGV(ppc_state));
|
||||
GenericValue lr_arg;
|
||||
lr_arg.IntVal = APInt(64, lr);
|
||||
args.push_back(lr_arg);
|
||||
GenericValue ret = engine_->runFunction(f, args);
|
||||
// return (uint32_t)ret.IntVal.getSExtValue();
|
||||
// Find the function to execute.
|
||||
FunctionPointer f = fn_table_->GetFunction(address);
|
||||
|
||||
// Faster, somewhat.
|
||||
// JIT, if needed.
|
||||
if (!f) {
|
||||
f = this->GenerateFunction(address);
|
||||
}
|
||||
|
||||
// If JIT failed, die.
|
||||
if (!f) {
|
||||
XELOGCPU("Execute(%.8X): failed to find function", address);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Execute the function pointer.
|
||||
// Messes with the stack in such a way as to cause Xcode to behave oddly.
|
||||
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
|
||||
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
|
||||
// ptr(ppc_state, lr);
|
||||
f(ppc_state, lr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -246,11 +250,3 @@ uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
|
|||
}
|
||||
return ppc_state->r[3];
|
||||
}
|
||||
|
||||
Function* Processor::GetFunction(uint32_t address) {
|
||||
FunctionMap::iterator it = all_fns_.find(address);
|
||||
if (it != all_fns_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -14,36 +14,33 @@
|
|||
|
||||
#include <vector>
|
||||
|
||||
#include <xenia/cpu/backend.h>
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/function_table.h>
|
||||
#include <xenia/cpu/thread_state.h>
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/xex2.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class Function;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
|
||||
class JIT;
|
||||
|
||||
|
||||
class Processor {
|
||||
public:
|
||||
Processor(xe_memory_ref memory);
|
||||
Processor(xe_memory_ref memory, shared_ptr<Backend> backend);
|
||||
~Processor();
|
||||
|
||||
xe_memory_ref memory();
|
||||
shared_ptr<kernel::ExportResolver> export_resolver();
|
||||
void set_export_resolver(shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
|
||||
int Setup();
|
||||
|
||||
int LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
|
||||
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
|
||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
||||
int LoadRawBinary(const xechar_t* path, uint32_t start_address);
|
||||
int LoadXexModule(const char* name, const char* path, xe_xex2_ref xex);
|
||||
|
||||
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
||||
|
||||
|
@ -53,16 +50,15 @@ public:
|
|||
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
||||
|
||||
private:
|
||||
llvm::Function* GetFunction(uint32_t address);
|
||||
FunctionPointer GenerateFunction(uint32_t address);
|
||||
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<llvm::ExecutionEngine> engine_;
|
||||
|
||||
auto_ptr<llvm::LLVMContext> dummy_context_;
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<Backend> backend_;
|
||||
shared_ptr<kernel::ExportResolver> export_resolver_;
|
||||
|
||||
FunctionTable* fn_table_;
|
||||
JIT* jit_;
|
||||
std::vector<ExecModule*> modules_;
|
||||
|
||||
FunctionMap all_fns_;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -105,6 +105,11 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionSymbol::AddCall(FunctionSymbol* source, FunctionSymbol* target) {
|
||||
source->outgoing_calls.push_back(FunctionCall(0, source, target));
|
||||
target->incoming_calls.push_back(FunctionCall(0, source, target));
|
||||
}
|
||||
|
||||
|
||||
VariableSymbol::VariableSymbol() :
|
||||
Symbol(Variable),
|
||||
|
|
|
@ -32,6 +32,10 @@ public:
|
|||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
FunctionSymbol* target;
|
||||
|
||||
FunctionCall(uint32_t address, FunctionSymbol* source,
|
||||
FunctionSymbol* target) :
|
||||
address(address), source(source), target(target) {}
|
||||
};
|
||||
|
||||
class VariableAccess {
|
||||
|
@ -39,6 +43,10 @@ public:
|
|||
uint32_t address;
|
||||
FunctionSymbol* source;
|
||||
VariableSymbol* target;
|
||||
|
||||
VariableAccess(uint32_t address, FunctionSymbol* source,
|
||||
VariableSymbol* target) :
|
||||
address(address), source(source), target(target) {}
|
||||
};
|
||||
|
||||
class Symbol {
|
||||
|
@ -116,11 +124,13 @@ public:
|
|||
kernel::KernelExport* kernel_export;
|
||||
ExceptionEntrySymbol* ee;
|
||||
|
||||
std::vector<FunctionCall*> incoming_calls;
|
||||
std::vector<FunctionCall*> outgoing_calls;
|
||||
std::vector<VariableAccess*> variable_accesses;
|
||||
std::vector<FunctionCall> incoming_calls;
|
||||
std::vector<FunctionCall> outgoing_calls;
|
||||
std::vector<VariableAccess> variable_accesses;
|
||||
|
||||
std::map<uint32_t, FunctionBlock*> blocks;
|
||||
|
||||
static void AddCall(FunctionSymbol* source, FunctionSymbol* target);
|
||||
};
|
||||
|
||||
class VariableSymbol : public Symbol {
|
||||
|
|
|
@ -103,9 +103,13 @@ ExceptionEntrySymbol* SymbolDatabase::GetOrInsertExceptionEntry(
|
|||
return ee;
|
||||
}
|
||||
|
||||
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
||||
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(
|
||||
uint32_t address, FunctionSymbol* opt_call_source) {
|
||||
FunctionSymbol* fn = GetFunction(address);
|
||||
if (fn) {
|
||||
if (opt_call_source) {
|
||||
FunctionSymbol::AddCall(opt_call_source, fn);
|
||||
}
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
@ -120,6 +124,11 @@ FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
|||
function_count_++;
|
||||
symbols_.insert(SymbolMap::value_type(address, fn));
|
||||
scan_queue_.push_back(fn);
|
||||
|
||||
if (opt_call_source) {
|
||||
FunctionSymbol::AddCall(opt_call_source, fn);
|
||||
}
|
||||
|
||||
return fn;
|
||||
}
|
||||
|
||||
|
@ -460,7 +469,9 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
|||
// Function call.
|
||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
||||
if (!block->outgoing_function) {
|
||||
if (block->outgoing_function) {
|
||||
FunctionSymbol::AddCall(fn, block->outgoing_function);
|
||||
} else {
|
||||
XELOGE("call target not found: %.8X -> %.8X",
|
||||
block->end_address, block->outgoing_address);
|
||||
new_fns.push_back(block->outgoing_address);
|
||||
|
@ -474,7 +485,7 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
|||
(uint32_t)new_fns.size());
|
||||
for (std::vector<uint32_t>::iterator it = new_fns.begin();
|
||||
it != new_fns.end(); ++it) {
|
||||
GetOrInsertFunction(*it);
|
||||
GetOrInsertFunction(*it, fn);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ public:
|
|||
|
||||
Symbol* GetSymbol(uint32_t address);
|
||||
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
|
||||
FunctionSymbol* GetOrInsertFunction(uint32_t address);
|
||||
FunctionSymbol* GetOrInsertFunction(
|
||||
uint32_t address, FunctionSymbol* opt_call_source = NULL);
|
||||
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
||||
FunctionSymbol* GetFunction(uint32_t address);
|
||||
VariableSymbol* GetVariable(uint32_t address);
|
||||
|
|
|
@ -1,13 +1,20 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'backend.h',
|
||||
'code_unit_builder.h',
|
||||
'cpu-private.h',
|
||||
'cpu.cc',
|
||||
'cpu.h',
|
||||
'exec_module.cc',
|
||||
'exec_module.h',
|
||||
'llvm_exports.cc',
|
||||
'llvm_exports.h',
|
||||
'function_table.cc',
|
||||
'function_table.h',
|
||||
'global_exports.cc',
|
||||
'global_exports.h',
|
||||
'jit.h',
|
||||
'library_linker.h',
|
||||
'library_loader.h',
|
||||
'ppc.h',
|
||||
'processor.cc',
|
||||
'processor.h',
|
||||
|
@ -16,7 +23,7 @@
|
|||
],
|
||||
|
||||
'includes': [
|
||||
'codegen/sources.gypi',
|
||||
'llvmbe/sources.gypi',
|
||||
'ppc/sources.gypi',
|
||||
'sdb/sources.gypi',
|
||||
],
|
||||
|
|
Binary file not shown.
|
@ -1,39 +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. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file is compiled with clang to produce LLVM bitcode.
|
||||
* When the emulator goes to build a full module it then imports this code into
|
||||
* the generated module to provide globals/other shared values.
|
||||
*
|
||||
* Changes to this file require building a new version and checking it into the
|
||||
* repo on a machine that has clang.
|
||||
*
|
||||
* # rebuild the xethunk.bc/.ll files:
|
||||
* xb xethunk
|
||||
*/
|
||||
|
||||
// NOTE: only headers in this directory should be included.
|
||||
#include "xethunk.h"
|
||||
|
||||
|
||||
// Global memory base.
|
||||
// Dereference + PPC address to manipulate memory. Note that it's stored in
|
||||
// big-endian!
|
||||
extern char* xe_memory_base;
|
||||
|
||||
|
||||
int xe_module_init() {
|
||||
// TODO(benvanik): setup call table, etc?
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_module_uninit() {
|
||||
}
|
|
@ -1,11 +0,0 @@
|
|||
; ModuleID = 'src/cpu/xethunk/xethunk.bc'
|
||||
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
|
||||
target triple = "x86_64-apple-macosx10.8.0"
|
||||
|
||||
define i32 @xe_module_init() nounwind uwtable ssp {
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @xe_module_uninit() nounwind uwtable ssp {
|
||||
ret void
|
||||
}
|
|
@ -28,6 +28,7 @@ public:
|
|||
|
||||
private:
|
||||
xe_memory_ref memory_;
|
||||
shared_ptr<Backend> backend_;
|
||||
shared_ptr<Processor> processor_;
|
||||
shared_ptr<Runtime> runtime_;
|
||||
shared_ptr<Debugger> debugger_;
|
||||
|
@ -45,6 +46,8 @@ int Run::Setup() {
|
|||
xe_zero_struct(&pal_options, sizeof(pal_options));
|
||||
XEEXPECTZERO(xe_pal_init(pal_options));
|
||||
|
||||
backend_ = shared_ptr<Backend>(new xe::cpu::llvmbe::LLVMBackend());
|
||||
|
||||
debugger_ = shared_ptr<Debugger>(new Debugger());
|
||||
|
||||
xe_memory_options_t memory_options;
|
||||
|
@ -52,10 +55,11 @@ int Run::Setup() {
|
|||
memory_ = xe_memory_create(memory_options);
|
||||
XEEXPECTNOTNULL(memory_);
|
||||
|
||||
processor_ = shared_ptr<Processor>(new Processor(memory_));
|
||||
processor_ = shared_ptr<Processor>(new Processor(memory_, backend_));
|
||||
XEEXPECTZERO(processor_->Setup());
|
||||
|
||||
runtime_ = shared_ptr<Runtime>(new Runtime(processor_, XT("")));
|
||||
processor_->set_export_resolver(runtime_->export_resolver());
|
||||
|
||||
return 0;
|
||||
XECLEANUP:
|
||||
|
|
Loading…
Reference in New Issue