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
|
||||||
|
|
||||||
|
jit:
|
||||||
|
- add imports/exports/etc to module
|
||||||
ExecModule:
|
- function generator (thread safe)
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ xe_pal_mac_t* pal;
|
||||||
|
|
||||||
void xe_pal_dealloc();
|
void xe_pal_dealloc();
|
||||||
int xe_pal_init(xe_pal_options_t options) {
|
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_data_t info;
|
||||||
mach_timebase_info(&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/ppc.h>
|
||||||
#include <xenia/cpu/codegen/function_generator.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.
|
// Build out all the user functions.
|
||||||
size_t n = 0;
|
size_t n = 0;
|
||||||
XELOGI("Beginning generation of %ld functions...", functions.size());
|
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) {
|
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||||
Module *m = gen_module_;
|
Module *m = gen_module_;
|
||||||
|
@ -288,48 +149,3 @@ void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
||||||
|
|
||||||
OptimizeFunction(m, f);
|
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>
|
#include <xenia/cpu/processor.h>
|
||||||
|
|
||||||
|
// TODO(benvanik): conditionally include?
|
||||||
|
#include <xenia/cpu/llvmbe/llvm_backend.h>
|
||||||
|
|
||||||
#endif // XENIA_CPU_CPU_H_
|
#endif // XENIA_CPU_CPU_H_
|
||||||
|
|
|
@ -9,67 +9,49 @@
|
||||||
|
|
||||||
#include <xenia/cpu/exec_module.h>
|
#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/cpu-private.h>
|
||||||
#include <xenia/cpu/llvm_exports.h>
|
|
||||||
#include <xenia/cpu/sdb.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;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::codegen;
|
|
||||||
using namespace xe::cpu::sdb;
|
using namespace xe::cpu::sdb;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
|
||||||
|
|
||||||
ExecModule::ExecModule(
|
ExecModule::ExecModule(
|
||||||
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
|
||||||
const char* module_name, const char* module_path,
|
FunctionTable* fn_table,
|
||||||
shared_ptr<llvm::ExecutionEngine>& engine) {
|
const char* module_name, const char* module_path) {
|
||||||
memory_ = xe_memory_retain(memory);
|
memory_ = xe_memory_retain(memory);
|
||||||
export_resolver_ = export_resolver;
|
export_resolver_ = export_resolver;
|
||||||
|
fn_table_ = fn_table;
|
||||||
module_name_ = xestrdupa(module_name);
|
module_name_ = xestrdupa(module_name);
|
||||||
module_path_ = xestrdupa(module_path);
|
module_path_ = xestrdupa(module_path);
|
||||||
engine_ = engine;
|
|
||||||
|
|
||||||
context_ = shared_ptr<LLVMContext>(new LLVMContext());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ExecModule::~ExecModule() {
|
ExecModule::~ExecModule() {
|
||||||
if (gen_module_) {
|
|
||||||
Uninit();
|
|
||||||
engine_->removeModule(gen_module_.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_free(module_path_);
|
xe_free(module_path_);
|
||||||
xe_free(module_name_);
|
xe_free(module_name_);
|
||||||
xe_memory_release(memory_);
|
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>(
|
sdb_ = shared_ptr<sdb::SymbolDatabase>(
|
||||||
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
|
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
|
||||||
|
|
||||||
|
@ -94,64 +76,19 @@ int ExecModule::PrepareXex(xe_xex2_ref xex) {
|
||||||
return result_code;
|
return result_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Import variables.
|
|
||||||
// TODO??
|
|
||||||
|
|
||||||
return 0;
|
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 ExecModule::Prepare() {
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
std::string error_message;
|
|
||||||
|
|
||||||
char file_name[XE_MAX_PATH];
|
char file_name[XE_MAX_PATH];
|
||||||
|
|
||||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
|
||||||
auto_ptr<Module> shared_module;
|
|
||||||
auto_ptr<raw_ostream> outs;
|
|
||||||
|
|
||||||
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.
|
// 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());
|
XEEXPECTZERO(sdb_->Analyze());
|
||||||
|
|
||||||
// Load a specified module map and diff.
|
// Load a specified module map and diff.
|
||||||
|
@ -166,124 +103,18 @@ int ExecModule::Prepare() {
|
||||||
sdb_->WriteMap(file_name);
|
sdb_->WriteMap(file_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize the module.
|
// If recompiling, setup a recompiler and run.
|
||||||
gen_module_ = shared_ptr<Module>(
|
// Note that this will just load the library if it's present and valid.
|
||||||
new Module(module_name_, *context_.get()));
|
// TODO(benvanik): recompiler logic.
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
|
||||||
|
|
||||||
// Initialize the module.
|
// Initialize the module.
|
||||||
XEEXPECTZERO(Init());
|
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;
|
result_code = 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
return result_code;
|
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() {
|
int ExecModule::Init() {
|
||||||
// Setup all kernel variables.
|
// Setup all kernel variables.
|
||||||
std::vector<VariableSymbol*> 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.
|
return 0;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int ExecModule::Uninit() {
|
FunctionSymbol* ExecModule::FindFunctionSymbol(uint32_t address) {
|
||||||
// Grab function and call it.
|
return sdb_->GetFunction(address);
|
||||||
Function* xe_module_uninit = gen_module_->getFunction("xe_module_uninit");
|
|
||||||
std::vector<GenericValue> args;
|
|
||||||
engine_->runFunction(xe_module_uninit, args);
|
|
||||||
|
|
||||||
// Run static destructors.
|
|
||||||
engine_->runStaticConstructorsDestructors(gen_module_.get(), true);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExecModule::Dump() {
|
void ExecModule::Dump() {
|
||||||
|
|
|
@ -13,68 +13,46 @@
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/function_table.h>
|
||||||
#include <xenia/cpu/sdb.h>
|
#include <xenia/cpu/sdb.h>
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class ExecutionEngine;
|
|
||||||
class Function;
|
|
||||||
class LLVMContext;
|
|
||||||
class Module;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace codegen {
|
|
||||||
class ModuleGenerator;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
|
||||||
namespace cpu {
|
|
||||||
|
|
||||||
|
|
||||||
typedef std::tr1::unordered_map<uint32_t, llvm::Function*> FunctionMap;
|
|
||||||
|
|
||||||
|
|
||||||
class ExecModule {
|
class ExecModule {
|
||||||
public:
|
public:
|
||||||
ExecModule(
|
ExecModule(
|
||||||
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
|
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
|
||||||
const char* module_name, const char* module_path,
|
FunctionTable* fn_table,
|
||||||
shared_ptr<llvm::ExecutionEngine>& engine);
|
const char* module_name, const char* module_path);
|
||||||
~ExecModule();
|
~ExecModule();
|
||||||
|
|
||||||
int PrepareXex(xe_xex2_ref xex);
|
sdb::SymbolDatabase* sdb();
|
||||||
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
|
|
||||||
|
|
||||||
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();
|
void Dump();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
int Prepare();
|
int Prepare();
|
||||||
int InjectGlobals();
|
|
||||||
int Init();
|
int Init();
|
||||||
int Uninit();
|
|
||||||
|
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
shared_ptr<kernel::ExportResolver> export_resolver_;
|
shared_ptr<kernel::ExportResolver> export_resolver_;
|
||||||
|
FunctionTable* fn_table_;
|
||||||
char* module_name_;
|
char* module_name_;
|
||||||
char* module_path_;
|
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_low_;
|
||||||
uint32_t code_addr_high_;
|
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_
|
#ifndef XENIA_CPU_LLVMBE_EMIT_H_
|
||||||
#define XENIA_CPU_CODEGEN_EMIT_H_
|
#define XENIA_CPU_LLVMBE_EMIT_H_
|
||||||
|
|
||||||
#include <xenia/cpu/ppc/instr.h>
|
#include <xenia/cpu/ppc/instr.h>
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace codegen {
|
namespace llvmbe {
|
||||||
|
|
||||||
|
|
||||||
void RegisterEmitCategoryALU();
|
void RegisterEmitCategoryALU();
|
||||||
|
@ -33,9 +33,9 @@ void RegisterEmitCategoryMemory();
|
||||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||||
|
|
||||||
|
|
||||||
} // namespace codegen
|
} // namespace llvmbe
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // 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>
|
#include <xenia/cpu/ppc/state.h>
|
||||||
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace xe::cpu::codegen;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
using namespace xe::cpu::sdb;
|
using namespace xe::cpu::sdb;
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace codegen {
|
namespace llvmbe {
|
||||||
|
|
||||||
|
|
||||||
int XeEmitIndirectBranchTo(
|
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) {
|
bool lk, uint32_t reg) {
|
||||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
// 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
|
// a normal function return that is pulling the LR from the stack that
|
||||||
|
@ -37,10 +37,10 @@ int XeEmitIndirectBranchTo(
|
||||||
Value* target;
|
Value* target;
|
||||||
switch (reg) {
|
switch (reg) {
|
||||||
case kXEPPCRegLR:
|
case kXEPPCRegLR:
|
||||||
target = g.lr_value();
|
target = e.lr_value();
|
||||||
break;
|
break;
|
||||||
case kXEPPCRegCTR:
|
case kXEPPCRegCTR:
|
||||||
target = g.ctr_value();
|
target = e.ctr_value();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
XEASSERTALWAYS();
|
XEASSERTALWAYS();
|
||||||
|
@ -52,29 +52,29 @@ int XeEmitIndirectBranchTo(
|
||||||
// Ideally it's a return and we can just do a simple ret and be done.
|
// 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 it's not, we fall through to the full indirection logic.
|
||||||
if (!lk && reg == kXEPPCRegLR) {
|
if (!lk && reg == kXEPPCRegLR) {
|
||||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||||
BasicBlock* mismatch_bb = BasicBlock::Create(*g.context(), "lr_mismatch",
|
BasicBlock* mismatch_bb = BasicBlock::Create(*e.context(), "lr_mismatch",
|
||||||
g.gen_fn(), next_block);
|
e.gen_fn(), next_block);
|
||||||
Value* lr_cmp = b.CreateICmpEQ(target, ++(g.gen_fn()->arg_begin()));
|
Value* lr_cmp = b.CreateICmpEQ(target, ++(e.gen_fn()->arg_begin()));
|
||||||
// The return block will spill registers for us.
|
// 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);
|
b.SetInsertPoint(mismatch_bb);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Defer to the generator, which will do fancy things.
|
// Defer to the generator, which will do fancy things.
|
||||||
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
||||||
return g.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
return e.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||||
}
|
}
|
||||||
|
|
||||||
int XeEmitBranchTo(
|
int XeEmitBranchTo(
|
||||||
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
|
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||||
bool lk) {
|
bool lk) {
|
||||||
// Get the basic block and switch behavior based on outgoing type.
|
// 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) {
|
switch (fn_block->outgoing_type) {
|
||||||
case FunctionBlock::kTargetBlock:
|
case FunctionBlock::kTargetBlock:
|
||||||
{
|
{
|
||||||
BasicBlock* target_bb = g.GetBasicBlock(fn_block->outgoing_address);
|
BasicBlock* target_bb = e.GetBasicBlock(fn_block->outgoing_address);
|
||||||
XEASSERTNOTNULL(target_bb);
|
XEASSERTNOTNULL(target_bb);
|
||||||
b.CreateBr(target_bb);
|
b.CreateBr(target_bb);
|
||||||
break;
|
break;
|
||||||
|
@ -85,13 +85,13 @@ int XeEmitBranchTo(
|
||||||
// TODO(benvanik): only spill ones used by the target function? Use
|
// TODO(benvanik): only spill ones used by the target function? Use
|
||||||
// calling convention flags on the function to not spill temp
|
// calling convention flags on the function to not spill temp
|
||||||
// registers?
|
// registers?
|
||||||
g.SpillRegisters();
|
e.SpillRegisters();
|
||||||
|
|
||||||
XEASSERTNOTNULL(fn_block->outgoing_function);
|
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||||
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
|
Function* target_fn = e.GetFunction(fn_block->outgoing_function);
|
||||||
Function::arg_iterator args = g.gen_fn()->arg_begin();
|
Function::arg_iterator args = e.gen_fn()->arg_begin();
|
||||||
Value* state_ptr = args;
|
Value* state_ptr = args;
|
||||||
BasicBlock* next_bb = g.GetNextBasicBlock();
|
BasicBlock* next_bb = e.GetNextBasicBlock();
|
||||||
if (!lk || !next_bb) {
|
if (!lk || !next_bb) {
|
||||||
// Tail. No need to refill the local register values, just return.
|
// 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
|
// We optimize this by passing in the LR from our parent instead of the
|
||||||
|
@ -103,7 +103,7 @@ int XeEmitBranchTo(
|
||||||
// Will return here eventually.
|
// Will return here eventually.
|
||||||
// Refill registers from state.
|
// Refill registers from state.
|
||||||
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
|
||||||
g.FillRegisters();
|
e.FillRegisters();
|
||||||
b.CreateBr(next_bb);
|
b.CreateBr(next_bb);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -112,13 +112,13 @@ int XeEmitBranchTo(
|
||||||
{
|
{
|
||||||
// An indirect jump.
|
// An indirect jump.
|
||||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
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:
|
case FunctionBlock::kTargetCTR:
|
||||||
{
|
{
|
||||||
// An indirect jump.
|
// An indirect jump.
|
||||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
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:
|
default:
|
||||||
case FunctionBlock::kTargetNone:
|
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
|
// if AA then
|
||||||
// NIA <- EXTS(LI || 0b00)
|
// NIA <- EXTS(LI || 0b00)
|
||||||
// else
|
// else
|
||||||
|
@ -144,13 +144,13 @@ XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
nia = i.address + XEEXTS26(i.I.LI << 2);
|
nia = i.address + XEEXTS26(i.I.LI << 2);
|
||||||
}
|
}
|
||||||
if (i.I.LK) {
|
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
|
// if ¬BO[2] then
|
||||||
// CTR <- CTR - 1
|
// CTR <- CTR - 1
|
||||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
|
// 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!
|
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||||
// The docs say always, though...
|
// The docs say always, though...
|
||||||
if (i.B.LK) {
|
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;
|
Value* ctr_ok = NULL;
|
||||||
|
@ -178,9 +178,9 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// Ignore ctr.
|
// Ignore ctr.
|
||||||
} else {
|
} else {
|
||||||
// Decrement counter.
|
// Decrement counter.
|
||||||
Value* ctr = g.ctr_value();
|
Value* ctr = e.ctr_value();
|
||||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||||
g.update_ctr_value(ctr);
|
e.update_ctr_value(ctr);
|
||||||
|
|
||||||
// Ctr check.
|
// Ctr check.
|
||||||
if (XESELECTBITS(i.B.BO, 1, 1)) {
|
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)) {
|
if (XESELECTBITS(i.B.BO, 4, 4)) {
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} 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));
|
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
|
||||||
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
if (XESELECTBITS(i.B.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
|
@ -218,8 +218,8 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
if (ok) {
|
if (ok) {
|
||||||
char name[32];
|
char name[32];
|
||||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
||||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||||
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
|
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||||
next_block);
|
next_block);
|
||||||
|
|
||||||
b.CreateCondBr(ok, branch_bb, next_block);
|
b.CreateCondBr(ok, branch_bb, next_block);
|
||||||
|
@ -233,7 +233,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
} else {
|
} else {
|
||||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,7 +242,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
return 0;
|
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])
|
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||||
// if cond_ok then
|
// if cond_ok then
|
||||||
// NIA <- CTR[0:61] || 0b00
|
// 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!
|
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||||
// The docs say always, though...
|
// The docs say always, though...
|
||||||
if (i.XL.LK) {
|
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;
|
Value* cond_ok = NULL;
|
||||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} 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));
|
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
|
@ -283,9 +283,9 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
if (ok) {
|
if (ok) {
|
||||||
char name[32];
|
char name[32];
|
||||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
|
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
|
||||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||||
XEASSERTNOTNULL(next_block);
|
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);
|
next_block);
|
||||||
|
|
||||||
b.CreateCondBr(ok, branch_bb, 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.
|
// 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -302,7 +302,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
return 0;
|
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
|
// if ¬BO[2] then
|
||||||
// CTR <- CTR - 1
|
// CTR <- CTR - 1
|
||||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
|
// 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!
|
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||||
// The docs say always, though...
|
// The docs say always, though...
|
||||||
if (i.XL.LK) {
|
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;
|
Value* ctr_ok = NULL;
|
||||||
|
@ -327,7 +327,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
// Ignore ctr.
|
// Ignore ctr.
|
||||||
} else {
|
} else {
|
||||||
// Decrement counter.
|
// Decrement counter.
|
||||||
Value* ctr = g.ctr_value();
|
Value* ctr = e.ctr_value();
|
||||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||||
|
|
||||||
// Ctr check.
|
// Ctr check.
|
||||||
|
@ -342,7 +342,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||||
// Ignore cond.
|
// Ignore cond.
|
||||||
} else {
|
} 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));
|
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
|
||||||
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
if (XESELECTBITS(i.XL.BO, 3, 3)) {
|
||||||
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
|
||||||
|
@ -366,9 +366,9 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
if (ok) {
|
if (ok) {
|
||||||
char name[32];
|
char name[32];
|
||||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
|
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
|
||||||
BasicBlock* next_block = g.GetNextBasicBlock();
|
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||||
XEASSERTNOTNULL(next_block);
|
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);
|
next_block);
|
||||||
|
|
||||||
b.CreateCondBr(ok, branch_bb, 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.
|
// 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;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -388,47 +388,47 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Condition register logical (A-23)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(crandc, 0x4C000102, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(crandc, 0x4C000102, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(creqv, 0x4C000242, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(creqv, 0x4C000242, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(crnand, 0x4C0001C2, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(crnand, 0x4C0001C2, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(crnor, 0x4C000042, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(crnor, 0x4C000042, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(cror, 0x4C000382, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(cror, 0x4C000382, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(crorc, 0x4C000342, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(crorc, 0x4C000342, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(crxor, 0x4C000182, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(crxor, 0x4C000182, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mcrf, 0x4C000000, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -436,7 +436,7 @@ XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// System linkage (A-24)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -444,7 +444,7 @@ XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Trap (A-25)
|
// 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) {
|
Value* va, Value* vb, uint32_t TO) {
|
||||||
// if (a < b) & TO[0] then TRAP
|
// if (a < b) & TO[0] then TRAP
|
||||||
// if (a > b) & TO[1] then TRAP
|
// if (a > b) & TO[1] then TRAP
|
||||||
|
@ -459,27 +459,27 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* after_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||||
g.GetNextBasicBlock());
|
e.GetNextBasicBlock());
|
||||||
BasicBlock* trap_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
|
BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||||
after_bb);
|
after_bb);
|
||||||
|
|
||||||
// Create the basic blocks (so we can chain).
|
// Create the basic blocks (so we can chain).
|
||||||
std::vector<BasicBlock*> bbs;
|
std::vector<BasicBlock*> bbs;
|
||||||
if (TO & (1 << 4)) {
|
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)) {
|
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)) {
|
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)) {
|
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)) {
|
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);
|
bbs.push_back(after_bb);
|
||||||
|
|
||||||
|
@ -526,10 +526,10 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
||||||
|
|
||||||
// Create trap BB.
|
// Create trap BB.
|
||||||
b.SetInsertPoint(trap_bb);
|
b.SetInsertPoint(trap_bb);
|
||||||
g.SpillRegisters();
|
e.SpillRegisters();
|
||||||
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
||||||
b.CreateCall2(g.gen_module()->getFunction("XeTrap"),
|
b.CreateCall2(e.gen_module()->getFunction("XeTrap"),
|
||||||
g.gen_fn()->arg_begin(),
|
e.gen_fn()->arg_begin(),
|
||||||
b.getInt32(i.address));
|
b.getInt32(i.address));
|
||||||
b.CreateBr(after_bb);
|
b.CreateBr(after_bb);
|
||||||
|
|
||||||
|
@ -539,7 +539,7 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(td, 0x7C000088, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
// a <- (RA)
|
// a <- (RA)
|
||||||
// b <- (RB)
|
// b <- (RB)
|
||||||
// if (a < b) & TO[0] then TRAP
|
// 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 = b) & TO[2] then TRAP
|
||||||
// if (a <u b) & TO[3] then TRAP
|
// if (a <u b) & TO[3] then TRAP
|
||||||
// if (a >u b) & TO[4] then TRAP
|
// if (a >u b) & TO[4] then TRAP
|
||||||
return XeEmitTrap(g, b, i,
|
return XeEmitTrap(e, b, i,
|
||||||
g.gpr_value(i.X.RA),
|
e.gpr_value(i.X.RA),
|
||||||
g.gpr_value(i.X.RB),
|
e.gpr_value(i.X.RB),
|
||||||
i.X.RT);
|
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)
|
// a <- (RA)
|
||||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||||
// if (a = EXTS(SI)) & TO[2] 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[3] then TRAP
|
||||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||||
return XeEmitTrap(g, b, i,
|
return XeEmitTrap(e, b, i,
|
||||||
g.gpr_value(i.D.RA),
|
e.gpr_value(i.D.RA),
|
||||||
b.getInt64(XEEXTS16(i.D.DS)),
|
b.getInt64(XEEXTS16(i.D.DS)),
|
||||||
i.D.RT);
|
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])
|
// a <- EXTS((RA)[32:63])
|
||||||
// b <- EXTS((RB)[32:63])
|
// b <- EXTS((RB)[32:63])
|
||||||
// if (a < b) & TO[0] then TRAP
|
// 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 = b) & TO[2] then TRAP
|
||||||
// if (a <u b) & TO[3] then TRAP
|
// if (a <u b) & TO[3] then TRAP
|
||||||
// if (a >u b) & TO[4] then TRAP
|
// if (a >u b) & TO[4] then TRAP
|
||||||
return XeEmitTrap(g, b, i,
|
return XeEmitTrap(e, b, i,
|
||||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RA),
|
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RA),
|
||||||
b.getInt32Ty()),
|
b.getInt32Ty()),
|
||||||
b.getInt64Ty()),
|
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.getInt32Ty()),
|
||||||
b.getInt64Ty()),
|
b.getInt64Ty()),
|
||||||
i.X.RT);
|
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])
|
// a <- EXTS((RA)[32:63])
|
||||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||||
// if (a = EXTS(SI)) & TO[2] 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[3] then TRAP
|
||||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||||
return XeEmitTrap(g, b, i,
|
return XeEmitTrap(e, b, i,
|
||||||
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.D.RA),
|
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.D.RA),
|
||||||
b.getInt32Ty()),
|
b.getInt32Ty()),
|
||||||
b.getInt64Ty()),
|
b.getInt64Ty()),
|
||||||
b.getInt64(XEEXTS16(i.D.DS)),
|
b.getInt64(XEEXTS16(i.D.DS)),
|
||||||
|
@ -602,12 +602,12 @@ XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Processor control (A-26)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
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]
|
// n <- spr[5:9] || spr[0:4]
|
||||||
// if length(SPR(n)) = 64 then
|
// if length(SPR(n)) = 64 then
|
||||||
// RT <- SPR(n)
|
// RT <- SPR(n)
|
||||||
|
@ -619,58 +619,58 @@ XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 1:
|
case 1:
|
||||||
// XER
|
// XER
|
||||||
v = g.xer_value();
|
v = e.xer_value();
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
// LR
|
// LR
|
||||||
v = g.lr_value();
|
v = e.lr_value();
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
// CTR
|
// CTR
|
||||||
v = g.ctr_value();
|
v = e.ctr_value();
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
g.update_gpr_value(i.XFX.RT, v);
|
e.update_gpr_value(i.XFX.RT, v);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mftb, 0x7C0002E6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mtcrf, 0x7C000120, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
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]
|
// n <- spr[5:9] || spr[0:4]
|
||||||
// if length(SPR(n)) = 64 then
|
// if length(SPR(n)) = 64 then
|
||||||
// SPR(n) <- (RS)
|
// SPR(n) <- (RS)
|
||||||
// else
|
// else
|
||||||
// SPR(n) <- (RS)[32:63]
|
// 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);
|
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 1:
|
case 1:
|
||||||
// XER
|
// XER
|
||||||
g.update_xer_value(v);
|
e.update_xer_value(v);
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
// LR
|
// LR
|
||||||
g.update_lr_value(v);
|
e.update_lr_value(v);
|
||||||
break;
|
break;
|
||||||
case 9:
|
case 9:
|
||||||
// CTR
|
// CTR
|
||||||
g.update_ctr_value(v);
|
e.update_ctr_value(v);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
|
@ -708,6 +708,6 @@ void RegisterEmitCategoryControl() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace codegen
|
} // namespace llvmbe
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // 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 llvm;
|
||||||
using namespace xe::cpu::codegen;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace codegen {
|
namespace llvmbe {
|
||||||
|
|
||||||
|
|
||||||
// Floating-point arithmetic (A-8)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(faddsx, 0xEC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(faddsx, 0xEC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fdivx, 0xFC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fdivx, 0xFC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fdivsx, 0xEC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fdivsx, 0xEC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmulx, 0xFC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmulx, 0xFC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmulsx, 0xEC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmulsx, 0xEC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fresx, 0xEC000030, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fresx, 0xEC000030, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(frsqrtex, 0xFC000034, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(frsqrtex, 0xFC000034, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fsubx, 0xFC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fsubx, 0xFC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fsubsx, 0xEC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fsubsx, 0xEC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fselx, 0xFC00002E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fselx, 0xFC00002E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fsqrtx, 0xFC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fsqrtsx, 0xEC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -92,42 +92,42 @@ XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Floating-point multiply-add (A-9)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmaddsx, 0xEC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmsubx, 0xFC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmsubx, 0xFC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmsubsx, 0xEC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmsubsx, 0xEC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnmaddx, 0xFC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnmaddsx, 0xEC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnmsubx, 0xFC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnmsubsx, 0xEC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -135,32 +135,32 @@ XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Floating-point rounding and conversion (A-10)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fctidx, 0xFC00065C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fctidx, 0xFC00065C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fctidzx, 0xFC00065E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fctidzx, 0xFC00065E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fctiwx, 0xFC00001C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fctiwx, 0xFC00001C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fctiwzx, 0xFC00001E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(frspx, 0xFC000018, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -168,12 +168,12 @@ XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Floating-point compare (A-11)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
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
|
// if (FRA) is a NaN or (FRB) is a NaN then
|
||||||
// c <- 0b0001
|
// c <- 0b0001
|
||||||
// else if (FRA) < (FRB) then
|
// 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
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mffsx, 0xFC00048E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mffsx, 0xFC00048E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mtfsb0x, 0xFC00008C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mtfsb1x, 0xFC00004C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(mtfsfix, 0xFC00010C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -227,22 +227,22 @@ XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
|
|
||||||
// Floating-point move (A-21)
|
// 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();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fmrx, 0xFC000090, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fmrx, 0xFC000090, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnabsx, 0xFC000110, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnabsx, 0xFC000110, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
XEEMITTER(fnegx, 0xFC000050, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(fnegx, 0xFC000050, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||||
XEINSTRNOTIMPLEMENTED();
|
XEINSTRNOTIMPLEMENTED();
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -291,6 +291,6 @@ void RegisterEmitCategoryFPU() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} // namespace codegen
|
} // namespace llvmbe
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // 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>
|
#include <llvm/IR/Intrinsics.h>
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@
|
||||||
|
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
using namespace xe::cpu::codegen;
|
using namespace xe::cpu::llvmbe;
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
using namespace xe::cpu::sdb;
|
using namespace xe::cpu::sdb;
|
||||||
|
|
||||||
|
@ -29,8 +29,9 @@ DEFINE_bool(log_codegen, false,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This generates function code.
|
* This generates function code.
|
||||||
* One context is created for each function to generate. Each basic block in
|
* One context is created and shared for each function to generate.
|
||||||
* the function is created and stashed in one pass, then filled in the next.
|
* 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
|
* 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
|
* 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(
|
EmitterContext::EmitterContext(
|
||||||
xe_memory_ref memory, SymbolDatabase* sdb, FunctionSymbol* fn,
|
xe_memory_ref memory,
|
||||||
LLVMContext* context, Module* gen_module, Function* gen_fn) {
|
LLVMContext* context, Module* gen_module) {
|
||||||
memory_ = memory;
|
memory_ = memory;
|
||||||
sdb_ = sdb;
|
|
||||||
fn_ = fn;
|
|
||||||
context_ = context;
|
context_ = context;
|
||||||
gen_module_ = gen_module;
|
gen_module_ = gen_module;
|
||||||
gen_fn_ = gen_fn;
|
|
||||||
builder_ = new IRBuilder<>(*context_);
|
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;
|
fn_block_ = NULL;
|
||||||
return_block_ = NULL;
|
return_block_ = NULL;
|
||||||
internal_indirection_block_ = NULL;
|
internal_indirection_block_ = NULL;
|
||||||
external_indirection_block_ = NULL;
|
external_indirection_block_ = NULL;
|
||||||
bb_ = NULL;
|
bb_ = NULL;
|
||||||
|
|
||||||
|
insert_points_.clear();
|
||||||
|
bbs_.clear();
|
||||||
|
|
||||||
|
cia_ = 0;
|
||||||
|
|
||||||
access_bits_.Clear();
|
access_bits_.Clear();
|
||||||
|
|
||||||
locals_.indirection_target = NULL;
|
locals_.indirection_target = NULL;
|
||||||
|
@ -80,53 +103,49 @@ FunctionGenerator::FunctionGenerator(
|
||||||
locals_.fpr[n] = NULL;
|
locals_.fpr[n] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fn) {
|
||||||
if (FLAGS_log_codegen) {
|
if (FLAGS_log_codegen) {
|
||||||
printf("%s:\n", fn->name());
|
printf("%s:\n", fn->name());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionGenerator::~FunctionGenerator() {
|
return 0;
|
||||||
delete builder_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolDatabase* FunctionGenerator::sdb() {
|
llvm::LLVMContext* EmitterContext::context() {
|
||||||
return sdb_;
|
|
||||||
}
|
|
||||||
|
|
||||||
FunctionSymbol* FunctionGenerator::fn() {
|
|
||||||
return fn_;
|
|
||||||
}
|
|
||||||
|
|
||||||
llvm::LLVMContext* FunctionGenerator::context() {
|
|
||||||
return context_;
|
return context_;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Module* FunctionGenerator::gen_module() {
|
llvm::Module* EmitterContext::gen_module() {
|
||||||
return gen_module_;
|
return gen_module_;
|
||||||
}
|
}
|
||||||
|
|
||||||
llvm::Function* FunctionGenerator::gen_fn() {
|
FunctionSymbol* EmitterContext::fn() {
|
||||||
|
return fn_;
|
||||||
|
}
|
||||||
|
|
||||||
|
llvm::Function* EmitterContext::gen_fn() {
|
||||||
return gen_fn_;
|
return gen_fn_;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionBlock* FunctionGenerator::fn_block() {
|
FunctionBlock* EmitterContext::fn_block() {
|
||||||
return fn_block_;
|
return fn_block_;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::PushInsertPoint() {
|
void EmitterContext::PushInsertPoint() {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
insert_points_.push_back(std::pair<BasicBlock*, BasicBlock::iterator>(
|
insert_points_.push_back(std::pair<BasicBlock*, BasicBlock::iterator>(
|
||||||
b.GetInsertBlock(), b.GetInsertPoint()));
|
b.GetInsertBlock(), b.GetInsertPoint()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::PopInsertPoint() {
|
void EmitterContext::PopInsertPoint() {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
std::pair<BasicBlock*, BasicBlock::iterator> back = insert_points_.back();
|
std::pair<BasicBlock*, BasicBlock::iterator> back = insert_points_.back();
|
||||||
b.SetInsertPoint(back.first, back.second);
|
b.SetInsertPoint(back.first, back.second);
|
||||||
insert_points_.pop_back();
|
insert_points_.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::GenerateBasicBlocks() {
|
void EmitterContext::GenerateBasicBlocks() {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
// Always add an entry block.
|
// Always add an entry block.
|
||||||
|
@ -178,7 +197,7 @@ void FunctionGenerator::GenerateBasicBlocks() {
|
||||||
GenerateSharedBlocks();
|
GenerateSharedBlocks();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::GenerateSharedBlocks() {
|
void EmitterContext::GenerateSharedBlocks() {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
Value* indirect_branch = gen_module_->getFunction("XeIndirectBranch");
|
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
|
// Create the basic block that will end up getting filled during
|
||||||
// generation.
|
// generation.
|
||||||
char name[32];
|
char name[32];
|
||||||
|
@ -271,7 +290,7 @@ int FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
void EmitterContext::GenerateBasicBlock(FunctionBlock* block) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
BasicBlock* bb = GetBasicBlock(block->start_address);
|
BasicBlock* bb = GetBasicBlock(block->start_address);
|
||||||
|
@ -337,7 +356,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
||||||
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
||||||
// ia >> 8, ia & 0xFF, ctx->cu));
|
// ia >> 8, ia & 0xFF, ctx->cu));
|
||||||
|
|
||||||
typedef int (*InstrEmitter)(FunctionGenerator& g, IRBuilder<>& b,
|
typedef int (*InstrEmitter)(EmitterContext& g, IRBuilder<>& b,
|
||||||
InstrData& i);
|
InstrData& i);
|
||||||
InstrEmitter emit = (InstrEmitter)i.type->emit;
|
InstrEmitter emit = (InstrEmitter)i.type->emit;
|
||||||
if (!i.type->emit || emit(*this, *builder_, i)) {
|
if (!i.type->emit || emit(*this, *builder_, i)) {
|
||||||
|
@ -371,7 +390,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
||||||
// TODO(benvanik): finish up BB
|
// 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);
|
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(address);
|
||||||
if (it != bbs_.end()) {
|
if (it != bbs_.end()) {
|
||||||
return it->second;
|
return it->second;
|
||||||
|
@ -379,7 +398,7 @@ BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* FunctionGenerator::GetNextBasicBlock() {
|
BasicBlock* EmitterContext::GetNextBasicBlock() {
|
||||||
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(
|
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(
|
||||||
fn_block_->start_address);
|
fn_block_->start_address);
|
||||||
++it;
|
++it;
|
||||||
|
@ -389,21 +408,48 @@ BasicBlock* FunctionGenerator::GetNextBasicBlock() {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
BasicBlock* FunctionGenerator::GetReturnBasicBlock() {
|
BasicBlock* EmitterContext::GetReturnBasicBlock() {
|
||||||
return return_block_;
|
return return_block_;
|
||||||
}
|
}
|
||||||
|
|
||||||
Function* FunctionGenerator::GetFunction(FunctionSymbol* fn) {
|
Function* EmitterContext::GetFunction(FunctionSymbol* symbol) {
|
||||||
Function* result = gen_module_->getFunction(StringRef(fn->name()));
|
StringRef fn_name(symbol->name());
|
||||||
if (!result) {
|
Function* fn = gen_module_->getFunction(fn_name);
|
||||||
XELOGE("Static function not found: %.8X %s",
|
if (fn) {
|
||||||
fn->start_address, fn->name());
|
return fn;
|
||||||
}
|
|
||||||
XEASSERTNOTNULL(result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
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 EmitterContext::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
||||||
bool lk, bool likely_local) {
|
bool lk, bool likely_local) {
|
||||||
// This function is called by the control emitters when they know that an
|
// This function is called by the control emitters when they know that an
|
||||||
// indirect branch is required.
|
// indirect branch is required.
|
||||||
|
@ -489,7 +535,7 @@ int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
|
Value* EmitterContext::LoadStateValue(uint32_t offset, Type* type,
|
||||||
const char* name) {
|
const char* name) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
PointerType* pointerTy = PointerType::getUnqual(type);
|
PointerType* pointerTy = PointerType::getUnqual(type);
|
||||||
|
@ -500,7 +546,7 @@ Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
|
||||||
return b.CreateLoad(ptr, name);
|
return b.CreateLoad(ptr, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
|
void EmitterContext::StoreStateValue(uint32_t offset, Type* type,
|
||||||
Value* value) {
|
Value* value) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
PointerType* pointerTy = PointerType::getUnqual(type);
|
PointerType* pointerTy = PointerType::getUnqual(type);
|
||||||
|
@ -511,7 +557,7 @@ void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
|
||||||
b.CreateStore(value, ptr);
|
b.CreateStore(value, ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::SetupLocals() {
|
void EmitterContext::SetupLocals() {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
uint64_t spr_t = access_bits_.spr;
|
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_;
|
IRBuilder<>& b = *builder_;
|
||||||
// Insert into the entry block.
|
// Insert into the entry block.
|
||||||
PushInsertPoint();
|
PushInsertPoint();
|
||||||
|
@ -569,11 +615,11 @@ Value* FunctionGenerator::SetupLocal(llvm::Type* type, const char* name) {
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::cia_value() {
|
Value* EmitterContext::cia_value() {
|
||||||
return builder_->getInt32(cia_);
|
return builder_->getInt32(cia_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::FillRegisters() {
|
void EmitterContext::FillRegisters() {
|
||||||
// This updates all of the local register values from the state memory.
|
// 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
|
// It should be called on function entry for initial setup and after any
|
||||||
// calls that may modify the registers.
|
// 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
|
// This flushes all local registers (if written) to the register bank and
|
||||||
// resets their values.
|
// resets their values.
|
||||||
//
|
//
|
||||||
|
@ -709,13 +755,13 @@ void FunctionGenerator::SpillRegisters() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::xer_value() {
|
Value* EmitterContext::xer_value() {
|
||||||
XEASSERTNOTNULL(locals_.xer);
|
XEASSERTNOTNULL(locals_.xer);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
return b.CreateLoad(locals_.xer);
|
return b.CreateLoad(locals_.xer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::update_xer_value(Value* value) {
|
void EmitterContext::update_xer_value(Value* value) {
|
||||||
XEASSERTNOTNULL(locals_.xer);
|
XEASSERTNOTNULL(locals_.xer);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -726,7 +772,7 @@ void FunctionGenerator::update_xer_value(Value* value) {
|
||||||
b.CreateStore(value, locals_.xer);
|
b.CreateStore(value, locals_.xer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::update_xer_with_overflow(Value* value) {
|
void EmitterContext::update_xer_with_overflow(Value* value) {
|
||||||
XEASSERTNOTNULL(locals_.xer);
|
XEASSERTNOTNULL(locals_.xer);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -743,7 +789,7 @@ void FunctionGenerator::update_xer_with_overflow(Value* value) {
|
||||||
b.CreateStore(xer, locals_.xer);
|
b.CreateStore(xer, locals_.xer);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::update_xer_with_carry(Value* value) {
|
void EmitterContext::update_xer_with_carry(Value* value) {
|
||||||
XEASSERTNOTNULL(locals_.xer);
|
XEASSERTNOTNULL(locals_.xer);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -759,7 +805,7 @@ void FunctionGenerator::update_xer_with_carry(Value* value) {
|
||||||
b.CreateStore(xer, locals_.xer);
|
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);
|
XEASSERTNOTNULL(locals_.xer);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -779,13 +825,13 @@ void FunctionGenerator::update_xer_with_overflow_and_carry(Value* value) {
|
||||||
b.CreateStore(xer, locals_.xer);
|
b.CreateStore(xer, locals_.xer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::lr_value() {
|
Value* EmitterContext::lr_value() {
|
||||||
XEASSERTNOTNULL(locals_.lr);
|
XEASSERTNOTNULL(locals_.lr);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
return b.CreateLoad(locals_.lr);
|
return b.CreateLoad(locals_.lr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::update_lr_value(Value* value) {
|
void EmitterContext::update_lr_value(Value* value) {
|
||||||
XEASSERTNOTNULL(locals_.lr);
|
XEASSERTNOTNULL(locals_.lr);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -796,14 +842,14 @@ void FunctionGenerator::update_lr_value(Value* value) {
|
||||||
b.CreateStore(value, locals_.lr);
|
b.CreateStore(value, locals_.lr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::ctr_value() {
|
Value* EmitterContext::ctr_value() {
|
||||||
XEASSERTNOTNULL(locals_.ctr);
|
XEASSERTNOTNULL(locals_.ctr);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
return b.CreateLoad(locals_.ctr);
|
return b.CreateLoad(locals_.ctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::update_ctr_value(Value* value) {
|
void EmitterContext::update_ctr_value(Value* value) {
|
||||||
XEASSERTNOTNULL(locals_.ctr);
|
XEASSERTNOTNULL(locals_.ctr);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -814,7 +860,7 @@ void FunctionGenerator::update_ctr_value(Value* value) {
|
||||||
b.CreateStore(value, locals_.ctr);
|
b.CreateStore(value, locals_.ctr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::cr_value(uint32_t n) {
|
Value* EmitterContext::cr_value(uint32_t n) {
|
||||||
XEASSERT(n >= 0 && n < 8);
|
XEASSERT(n >= 0 && n < 8);
|
||||||
XEASSERTNOTNULL(locals_.cr[n]);
|
XEASSERTNOTNULL(locals_.cr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
@ -824,7 +870,7 @@ Value* FunctionGenerator::cr_value(uint32_t n) {
|
||||||
return v;
|
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);
|
XEASSERT(n >= 0 && n < 8);
|
||||||
XEASSERTNOTNULL(locals_.cr[n]);
|
XEASSERTNOTNULL(locals_.cr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
@ -838,7 +884,7 @@ void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
|
||||||
b.CreateStore(value, locals_.cr[n]);
|
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) {
|
uint32_t n, Value* lhs, Value* rhs, bool is_signed) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -870,7 +916,7 @@ void FunctionGenerator::update_cr_with_cond(
|
||||||
update_cr_value(n, c);
|
update_cr_value(n, c);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::gpr_value(uint32_t n) {
|
Value* EmitterContext::gpr_value(uint32_t n) {
|
||||||
XEASSERT(n >= 0 && n < 32);
|
XEASSERT(n >= 0 && n < 32);
|
||||||
XEASSERTNOTNULL(locals_.gpr[n]);
|
XEASSERTNOTNULL(locals_.gpr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
@ -885,7 +931,7 @@ Value* FunctionGenerator::gpr_value(uint32_t n) {
|
||||||
return b.CreateLoad(locals_.gpr[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);
|
XEASSERT(n >= 0 && n < 32);
|
||||||
XEASSERTNOTNULL(locals_.gpr[n]);
|
XEASSERTNOTNULL(locals_.gpr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
@ -904,14 +950,14 @@ void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
|
||||||
b.CreateStore(value, locals_.gpr[n]);
|
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);
|
XEASSERT(n >= 0 && n < 32);
|
||||||
XEASSERTNOTNULL(locals_.fpr[n]);
|
XEASSERTNOTNULL(locals_.fpr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
return b.CreateLoad(locals_.fpr[n]);
|
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);
|
XEASSERT(n >= 0 && n < 32);
|
||||||
XEASSERTNOTNULL(locals_.fpr[n]);
|
XEASSERTNOTNULL(locals_.fpr[n]);
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
@ -919,12 +965,12 @@ void FunctionGenerator::update_fpr_value(uint32_t n, Value* value) {
|
||||||
b.CreateStore(value, locals_.fpr[n]);
|
b.CreateStore(value, locals_.fpr[n]);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::GetMembase() {
|
Value* EmitterContext::GetMembase() {
|
||||||
Value* v = gen_module_->getGlobalVariable("xe_memory_base");
|
Value* v = gen_module_->getGlobalVariable("xe_memory_base");
|
||||||
return builder_->CreateLoad(v);
|
return builder_->CreateLoad(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::GetMemoryAddress(uint32_t cia, Value* addr) {
|
Value* EmitterContext::GetMemoryAddress(uint32_t cia, Value* addr) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
// Input address is always in 32-bit space.
|
// 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);
|
return b.CreateInBoundsGEP(GetMembase(), addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
Value* FunctionGenerator::ReadMemory(
|
Value* EmitterContext::ReadMemory(
|
||||||
uint32_t cia, Value* addr, uint32_t size, bool acquire) {
|
uint32_t cia, Value* addr, uint32_t size, bool acquire) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
@ -1004,7 +1050,7 @@ Value* FunctionGenerator::ReadMemory(
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::WriteMemory(
|
void EmitterContext::WriteMemory(
|
||||||
uint32_t cia, Value* addr, uint32_t size, Value* value, bool release) {
|
uint32_t cia, Value* addr, uint32_t size, Value* value, bool release) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
|
@ -7,8 +7,8 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
#ifndef XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
||||||
#define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
|
#define XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
||||||
|
|
||||||
#include <llvm/IR/Attributes.h>
|
#include <llvm/IR/Attributes.h>
|
||||||
#include <llvm/IR/DataLayout.h>
|
#include <llvm/IR/DataLayout.h>
|
||||||
|
@ -23,21 +23,22 @@
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
namespace codegen {
|
namespace llvmbe {
|
||||||
|
|
||||||
|
|
||||||
class FunctionGenerator {
|
class EmitterContext {
|
||||||
public:
|
public:
|
||||||
FunctionGenerator(
|
EmitterContext(
|
||||||
xe_memory_ref memory, sdb::SymbolDatabase* sdb, sdb::FunctionSymbol* fn,
|
xe_memory_ref memory,
|
||||||
llvm::LLVMContext* context, llvm::Module* gen_module,
|
llvm::LLVMContext* context, llvm::Module* gen_module);
|
||||||
llvm::Function* gen_fn);
|
~EmitterContext();
|
||||||
~FunctionGenerator();
|
|
||||||
|
int Init(sdb::FunctionSymbol* fn, llvm::Function* gen_fn);
|
||||||
|
|
||||||
sdb::SymbolDatabase* sdb();
|
|
||||||
sdb::FunctionSymbol* fn();
|
|
||||||
llvm::LLVMContext* context();
|
llvm::LLVMContext* context();
|
||||||
llvm::Module* gen_module();
|
llvm::Module* gen_module();
|
||||||
|
|
||||||
|
sdb::FunctionSymbol* fn();
|
||||||
llvm::Function* gen_fn();
|
llvm::Function* gen_fn();
|
||||||
sdb::FunctionBlock* fn_block();
|
sdb::FunctionBlock* fn_block();
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ public:
|
||||||
llvm::BasicBlock* GetNextBasicBlock();
|
llvm::BasicBlock* GetNextBasicBlock();
|
||||||
llvm::BasicBlock* GetReturnBasicBlock();
|
llvm::BasicBlock* GetReturnBasicBlock();
|
||||||
|
|
||||||
llvm::Function* GetFunction(sdb::FunctionSymbol* fn);
|
llvm::Function* GetFunction(sdb::FunctionSymbol* symbol);
|
||||||
|
|
||||||
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
|
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
|
||||||
bool lk, bool likely_local);
|
bool lk, bool likely_local);
|
||||||
|
@ -101,17 +102,18 @@ private:
|
||||||
void SetupLocals();
|
void SetupLocals();
|
||||||
|
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
sdb::SymbolDatabase* sdb_;
|
|
||||||
sdb::FunctionSymbol* fn_;
|
|
||||||
llvm::LLVMContext* context_;
|
llvm::LLVMContext* context_;
|
||||||
llvm::Module* gen_module_;
|
llvm::Module* gen_module_;
|
||||||
|
llvm::IRBuilder<>* builder_;
|
||||||
|
llvm::FunctionType* fn_type_;
|
||||||
|
|
||||||
|
sdb::FunctionSymbol* fn_;
|
||||||
llvm::Function* gen_fn_;
|
llvm::Function* gen_fn_;
|
||||||
sdb::FunctionBlock* fn_block_;
|
sdb::FunctionBlock* fn_block_;
|
||||||
llvm::BasicBlock* return_block_;
|
llvm::BasicBlock* return_block_;
|
||||||
llvm::BasicBlock* internal_indirection_block_;
|
llvm::BasicBlock* internal_indirection_block_;
|
||||||
llvm::BasicBlock* external_indirection_block_;
|
llvm::BasicBlock* external_indirection_block_;
|
||||||
llvm::BasicBlock* bb_;
|
llvm::BasicBlock* bb_;
|
||||||
llvm::IRBuilder<>* builder_;
|
|
||||||
|
|
||||||
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
|
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
|
||||||
insert_points_;
|
insert_points_;
|
||||||
|
@ -136,9 +138,9 @@ private:
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace codegen
|
} // namespace llvmbe
|
||||||
} // namespace cpu
|
} // namespace cpu
|
||||||
} // namespace xe
|
} // 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/ExecutionEngine/ExecutionEngine.h>
|
||||||
#include <llvm/IR/Constants.h>
|
#include <llvm/IR/Constants.h>
|
||||||
|
@ -16,95 +16,15 @@
|
||||||
#include <llvm/IR/LLVMContext.h>
|
#include <llvm/IR/LLVMContext.h>
|
||||||
#include <llvm/IR/Module.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 llvm;
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
using namespace xe::cpu::sdb;
|
|
||||||
using namespace xe::kernel;
|
|
||||||
|
|
||||||
|
|
||||||
namespace {
|
void xe::cpu::llvmbe::SetupLlvmExports(
|
||||||
|
GlobalExports* global_exports,
|
||||||
|
Module* module, const DataLayout* dl, ExecutionEngine* engine) {
|
||||||
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) {
|
|
||||||
LLVMContext& context = module->getContext();
|
LLVMContext& context = module->getContext();
|
||||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||||
|
|
||||||
|
@ -116,7 +36,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||||
Type::getVoidTy(context), trapArgs, false);
|
Type::getVoidTy(context), trapArgs, false);
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
trapTy, Function::ExternalLinkage, "XeTrap",
|
trapTy, Function::ExternalLinkage, "XeTrap",
|
||||||
module), (void*)&XeTrap);
|
module), (void*)(global_exports->XeTrap));
|
||||||
|
|
||||||
std::vector<Type*> indirectBranchArgs;
|
std::vector<Type*> indirectBranchArgs;
|
||||||
indirectBranchArgs.push_back(int8PtrTy);
|
indirectBranchArgs.push_back(int8PtrTy);
|
||||||
|
@ -126,7 +46,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||||
Type::getVoidTy(context), indirectBranchArgs, false);
|
Type::getVoidTy(context), indirectBranchArgs, false);
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
|
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
|
||||||
module), (void*)&XeIndirectBranch);
|
module), (void*)(global_exports->XeIndirectBranch));
|
||||||
|
|
||||||
// Debugging methods:
|
// Debugging methods:
|
||||||
std::vector<Type*> invalidInstructionArgs;
|
std::vector<Type*> invalidInstructionArgs;
|
||||||
|
@ -137,7 +57,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||||
Type::getVoidTy(context), invalidInstructionArgs, false);
|
Type::getVoidTy(context), invalidInstructionArgs, false);
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
|
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
|
||||||
module), (void*)&XeInvalidInstruction);
|
module), (void*)(global_exports->XeInvalidInstruction));
|
||||||
|
|
||||||
std::vector<Type*> accessViolationArgs;
|
std::vector<Type*> accessViolationArgs;
|
||||||
accessViolationArgs.push_back(int8PtrTy);
|
accessViolationArgs.push_back(int8PtrTy);
|
||||||
|
@ -147,7 +67,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||||
Type::getVoidTy(context), accessViolationArgs, false);
|
Type::getVoidTy(context), accessViolationArgs, false);
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
|
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
|
||||||
module), (void*)&XeAccessViolation);
|
module), (void*)(global_exports->XeAccessViolation));
|
||||||
|
|
||||||
// Tracing methods:
|
// Tracing methods:
|
||||||
std::vector<Type*> traceCallArgs;
|
std::vector<Type*> traceCallArgs;
|
||||||
|
@ -166,11 +86,11 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
|
||||||
|
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
|
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
|
||||||
module), (void*)&XeTraceKernelCall);
|
module), (void*)(global_exports->XeTraceKernelCall));
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
|
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
|
||||||
module), (void*)&XeTraceUserCall);
|
module), (void*)(global_exports->XeTraceUserCall));
|
||||||
engine->addGlobalMapping(Function::Create(
|
engine->addGlobalMapping(Function::Create(
|
||||||
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
|
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
|
||||||
module), (void*)&XeTraceInstruction);
|
module), (void*)(global_exports->XeTraceInstruction));
|
||||||
}
|
}
|
|
@ -7,16 +7,17 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef XENIA_CPU_LLVM_EXPORTS_H_
|
#ifndef XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
||||||
#define XENIA_CPU_LLVM_EXPORTS_H_
|
#define XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
||||||
|
|
||||||
#include <xenia/common.h>
|
#include <xenia/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <xenia/cpu/global_exports.h>
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
class ExecutionEngine;
|
class ExecutionEngine;
|
||||||
class LLVMContext;
|
|
||||||
class Module;
|
class Module;
|
||||||
class DataLayout;
|
class DataLayout;
|
||||||
}
|
}
|
||||||
|
@ -24,15 +25,18 @@ namespace llvm {
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
namespace llvmbe {
|
||||||
|
|
||||||
|
|
||||||
void SetupLlvmExports(llvm::Module* module,
|
void SetupLlvmExports(
|
||||||
const llvm::DataLayout* dl,
|
GlobalExports* global_exports,
|
||||||
|
llvm::Module* module, const llvm::DataLayout* dl,
|
||||||
llvm::ExecutionEngine* engine);
|
llvm::ExecutionEngine* engine);
|
||||||
|
|
||||||
|
|
||||||
|
} // llvmbe
|
||||||
} // cpu
|
} // cpu
|
||||||
} // xe
|
} // 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 @@
|
||||||
******************************************************************************
|
******************************************************************************
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
#include <xenia/cpu/llvmbe/llvm_library_linker.h>
|
||||||
* 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_
|
|
||||||
|
|
||||||
|
|
||||||
#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
|
// TODO
|
||||||
return d.Finish();
|
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) {
|
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
|
||||||
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
|
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
|
||||||
// TODO
|
// TODO
|
||||||
return d.Finish();
|
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) {
|
XEDISASMR(dcbz, 0x7C0007EC, X )(InstrData& i, InstrDisasm& d) {
|
||||||
// or dcbz128 0x7C2007EC
|
// or dcbz128 0x7C2007EC
|
||||||
|
|
|
@ -9,19 +9,10 @@
|
||||||
|
|
||||||
#include <xenia/cpu/processor.h>
|
#include <xenia/cpu/processor.h>
|
||||||
|
|
||||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
#include <xenia/cpu/jit.h>
|
||||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
#include <xenia/cpu/ppc/disasm.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>
|
|
||||||
|
|
||||||
|
|
||||||
using namespace llvm;
|
|
||||||
using namespace xe;
|
using namespace xe;
|
||||||
using namespace xe::cpu;
|
using namespace xe::cpu;
|
||||||
using namespace xe::kernel;
|
using namespace xe::kernel;
|
||||||
|
@ -38,36 +29,23 @@ namespace {
|
||||||
}
|
}
|
||||||
has_initialized = true;
|
has_initialized = true;
|
||||||
|
|
||||||
// TODO(benvanik): only do this once
|
|
||||||
LLVMLinkInInterpreter();
|
|
||||||
LLVMLinkInJIT();
|
|
||||||
InitializeNativeTarget();
|
|
||||||
|
|
||||||
llvm_start_multithreaded();
|
|
||||||
|
|
||||||
ppc::RegisterDisasmCategoryALU();
|
ppc::RegisterDisasmCategoryALU();
|
||||||
ppc::RegisterDisasmCategoryControl();
|
ppc::RegisterDisasmCategoryControl();
|
||||||
ppc::RegisterDisasmCategoryFPU();
|
ppc::RegisterDisasmCategoryFPU();
|
||||||
ppc::RegisterDisasmCategoryMemory();
|
ppc::RegisterDisasmCategoryMemory();
|
||||||
|
|
||||||
// TODO(benvanik): only do this once
|
|
||||||
codegen::RegisterEmitCategoryALU();
|
|
||||||
codegen::RegisterEmitCategoryControl();
|
|
||||||
codegen::RegisterEmitCategoryFPU();
|
|
||||||
codegen::RegisterEmitCategoryMemory();
|
|
||||||
|
|
||||||
atexit(CleanupOnShutdown);
|
atexit(CleanupOnShutdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CleanupOnShutdown() {
|
void CleanupOnShutdown() {
|
||||||
llvm_shutdown();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
|
Processor::Processor(xe_memory_ref memory, shared_ptr<Backend> backend) :
|
||||||
pal_ = xe_pal_retain(pal);
|
fn_table_(NULL), jit_(NULL) {
|
||||||
memory_ = xe_memory_retain(memory);
|
memory_ = xe_memory_retain(memory);
|
||||||
|
backend_ = backend;
|
||||||
|
|
||||||
InitializeIfNeeded();
|
InitializeIfNeeded();
|
||||||
}
|
}
|
||||||
|
@ -76,55 +54,55 @@ Processor::~Processor() {
|
||||||
// Cleanup all modules.
|
// Cleanup all modules.
|
||||||
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
for (std::vector<ExecModule*>::iterator it = modules_.begin();
|
||||||
it != modules_.end(); ++it) {
|
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_memory_release(memory_);
|
||||||
xe_pal_release(pal_);
|
|
||||||
}
|
|
||||||
|
|
||||||
xe_pal_ref Processor::pal() {
|
|
||||||
return xe_pal_retain(pal_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
xe_memory_ref Processor::memory() {
|
xe_memory_ref Processor::memory() {
|
||||||
return xe_memory_retain(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() {
|
int Processor::Setup() {
|
||||||
XEASSERTNULL(engine_);
|
XEASSERTNULL(jit_);
|
||||||
|
|
||||||
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
|
fn_table_ = new FunctionTable();
|
||||||
Module* dummy_module = new Module("dummy", *dummy_context_.get());
|
|
||||||
|
|
||||||
std::string error_message;
|
jit_ = backend_->CreateJIT(memory_, fn_table_);
|
||||||
|
if (jit_->Setup()) {
|
||||||
EngineBuilder builder(dummy_module);
|
XELOGE("Unable to create JIT");
|
||||||
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_) {
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
|
||||||
shared_ptr<ExportResolver> export_resolver) {
|
|
||||||
ExecModule* exec_module = NULL;
|
ExecModule* exec_module = NULL;
|
||||||
const xechar_t* name = xestrrchr(path, '/') + 1;
|
const xechar_t* name = xestrrchr(path, '/') + 1;
|
||||||
|
|
||||||
// TODO(benvanik): map file from filesystem
|
// TODO(benvanik): map file from filesystem API, not via platform API.
|
||||||
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
|
xe_mmap_ref mmap = xe_mmap_open(kXEFileModeRead, path, 0, 0);
|
||||||
if (!mmap) {
|
if (!mmap) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -133,30 +111,29 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
|
||||||
|
|
||||||
int result_code = 1;
|
int result_code = 1;
|
||||||
|
|
||||||
|
// Place the data into memory at the desired address.
|
||||||
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
|
||||||
xe_memory_get_length(memory_),
|
xe_memory_get_length(memory_),
|
||||||
addr, length));
|
addr, length));
|
||||||
|
|
||||||
// Prepare the module.
|
|
||||||
char name_a[XE_MAX_PATH];
|
char name_a[XE_MAX_PATH];
|
||||||
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
|
||||||
char path_a[XE_MAX_PATH];
|
char path_a[XE_MAX_PATH];
|
||||||
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), 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(
|
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,
|
// Initialize the module and prepare it for execution.
|
||||||
start_address + (uint32_t)length)) {
|
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||||
delete exec_module;
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_module->AddFunctionsToMap(all_fns_);
|
|
||||||
modules_.push_back(exec_module);
|
modules_.push_back(exec_module);
|
||||||
|
|
||||||
exec_module->Dump();
|
|
||||||
|
|
||||||
result_code = 0;
|
result_code = 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
if (result_code) {
|
if (result_code) {
|
||||||
|
@ -166,22 +143,28 @@ XECLEANUP:
|
||||||
return result_code;
|
return result_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::PrepareModule(const char* name, const char* path,
|
int Processor::LoadXexModule(const char* name, const char* path,
|
||||||
xe_xex2_ref xex,
|
xe_xex2_ref xex) {
|
||||||
shared_ptr<ExportResolver> export_resolver) {
|
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(
|
ExecModule* exec_module = new ExecModule(
|
||||||
memory_, export_resolver, name, path,
|
memory_, export_resolver_, fn_table_, name, path);
|
||||||
engine_);
|
XEEXPECTZERO(exec_module->PrepareXexModule(xex));
|
||||||
|
|
||||||
if (exec_module->PrepareXex(xex)) {
|
// Initialize the module and prepare it for execution.
|
||||||
delete exec_module;
|
XEEXPECTZERO(jit_->InitModule(exec_module));
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
exec_module->AddFunctionsToMap(all_fns_);
|
|
||||||
modules_.push_back(exec_module);
|
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) {
|
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
|
||||||
|
@ -200,14 +183,34 @@ void Processor::DeallocThread(ThreadState* thread_state) {
|
||||||
delete thread_state;
|
delete thread_state;
|
||||||
}
|
}
|
||||||
|
|
||||||
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
|
FunctionPointer Processor::GenerateFunction(uint32_t address) {
|
||||||
// Find the function to execute.
|
// Search all modules for the function symbol.
|
||||||
Function* f = GetFunction(address);
|
// Each module will see if the address is within its code range and if the
|
||||||
if (!f) {
|
// symbol is not found (likely) it will do analysis on it.
|
||||||
XELOGCPU("Failed to find function %.8X to execute.", address);
|
// TODO(benvanik): make this more efficient. Could use a binary search or
|
||||||
return 1;
|
// 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();
|
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
|
||||||
|
|
||||||
// This could be set to anything to give us a unique identifier to track
|
// 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.
|
// Setup registers.
|
||||||
ppc_state->lr = lr;
|
ppc_state->lr = lr;
|
||||||
|
|
||||||
// Args:
|
// Find the function to execute.
|
||||||
// - i8* state
|
FunctionPointer f = fn_table_->GetFunction(address);
|
||||||
// - 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();
|
|
||||||
|
|
||||||
// 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.
|
// Messes with the stack in such a way as to cause Xcode to behave oddly.
|
||||||
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
|
f(ppc_state, lr);
|
||||||
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
|
|
||||||
// ptr(ppc_state, lr);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -246,11 +250,3 @@ uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
|
||||||
}
|
}
|
||||||
return ppc_state->r[3];
|
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 <vector>
|
||||||
|
|
||||||
|
#include <xenia/cpu/backend.h>
|
||||||
#include <xenia/cpu/exec_module.h>
|
#include <xenia/cpu/exec_module.h>
|
||||||
|
#include <xenia/cpu/function_table.h>
|
||||||
#include <xenia/cpu/thread_state.h>
|
#include <xenia/cpu/thread_state.h>
|
||||||
#include <xenia/kernel/export.h>
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/xex2.h>
|
#include <xenia/kernel/xex2.h>
|
||||||
|
|
||||||
|
|
||||||
namespace llvm {
|
|
||||||
class ExecutionEngine;
|
|
||||||
class Function;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
namespace xe {
|
namespace xe {
|
||||||
namespace cpu {
|
namespace cpu {
|
||||||
|
|
||||||
|
class JIT;
|
||||||
|
|
||||||
|
|
||||||
class Processor {
|
class Processor {
|
||||||
public:
|
public:
|
||||||
Processor(xe_memory_ref memory);
|
Processor(xe_memory_ref memory, shared_ptr<Backend> backend);
|
||||||
~Processor();
|
~Processor();
|
||||||
|
|
||||||
xe_memory_ref memory();
|
xe_memory_ref memory();
|
||||||
|
shared_ptr<kernel::ExportResolver> export_resolver();
|
||||||
|
void set_export_resolver(shared_ptr<kernel::ExportResolver> export_resolver);
|
||||||
|
|
||||||
int Setup();
|
int Setup();
|
||||||
|
|
||||||
int LoadBinary(const xechar_t* path, uint32_t start_address,
|
int LoadRawBinary(const xechar_t* path, uint32_t start_address);
|
||||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
int LoadXexModule(const char* name, const char* path, xe_xex2_ref xex);
|
||||||
|
|
||||||
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
|
|
||||||
shared_ptr<kernel::ExportResolver> export_resolver);
|
|
||||||
|
|
||||||
uint32_t CreateCallback(void (*callback)(void* data), void* data);
|
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);
|
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
llvm::Function* GetFunction(uint32_t address);
|
FunctionPointer GenerateFunction(uint32_t address);
|
||||||
|
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
shared_ptr<llvm::ExecutionEngine> engine_;
|
shared_ptr<Backend> backend_;
|
||||||
|
shared_ptr<kernel::ExportResolver> export_resolver_;
|
||||||
auto_ptr<llvm::LLVMContext> dummy_context_;
|
|
||||||
|
|
||||||
|
FunctionTable* fn_table_;
|
||||||
|
JIT* jit_;
|
||||||
std::vector<ExecModule*> modules_;
|
std::vector<ExecModule*> modules_;
|
||||||
|
|
||||||
FunctionMap all_fns_;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -105,6 +105,11 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
|
||||||
return NULL;
|
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() :
|
VariableSymbol::VariableSymbol() :
|
||||||
Symbol(Variable),
|
Symbol(Variable),
|
||||||
|
|
|
@ -32,6 +32,10 @@ public:
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
FunctionSymbol* source;
|
FunctionSymbol* source;
|
||||||
FunctionSymbol* target;
|
FunctionSymbol* target;
|
||||||
|
|
||||||
|
FunctionCall(uint32_t address, FunctionSymbol* source,
|
||||||
|
FunctionSymbol* target) :
|
||||||
|
address(address), source(source), target(target) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariableAccess {
|
class VariableAccess {
|
||||||
|
@ -39,6 +43,10 @@ public:
|
||||||
uint32_t address;
|
uint32_t address;
|
||||||
FunctionSymbol* source;
|
FunctionSymbol* source;
|
||||||
VariableSymbol* target;
|
VariableSymbol* target;
|
||||||
|
|
||||||
|
VariableAccess(uint32_t address, FunctionSymbol* source,
|
||||||
|
VariableSymbol* target) :
|
||||||
|
address(address), source(source), target(target) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Symbol {
|
class Symbol {
|
||||||
|
@ -116,11 +124,13 @@ public:
|
||||||
kernel::KernelExport* kernel_export;
|
kernel::KernelExport* kernel_export;
|
||||||
ExceptionEntrySymbol* ee;
|
ExceptionEntrySymbol* ee;
|
||||||
|
|
||||||
std::vector<FunctionCall*> incoming_calls;
|
std::vector<FunctionCall> incoming_calls;
|
||||||
std::vector<FunctionCall*> outgoing_calls;
|
std::vector<FunctionCall> outgoing_calls;
|
||||||
std::vector<VariableAccess*> variable_accesses;
|
std::vector<VariableAccess> variable_accesses;
|
||||||
|
|
||||||
std::map<uint32_t, FunctionBlock*> blocks;
|
std::map<uint32_t, FunctionBlock*> blocks;
|
||||||
|
|
||||||
|
static void AddCall(FunctionSymbol* source, FunctionSymbol* target);
|
||||||
};
|
};
|
||||||
|
|
||||||
class VariableSymbol : public Symbol {
|
class VariableSymbol : public Symbol {
|
||||||
|
|
|
@ -103,9 +103,13 @@ ExceptionEntrySymbol* SymbolDatabase::GetOrInsertExceptionEntry(
|
||||||
return ee;
|
return ee;
|
||||||
}
|
}
|
||||||
|
|
||||||
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(
|
||||||
|
uint32_t address, FunctionSymbol* opt_call_source) {
|
||||||
FunctionSymbol* fn = GetFunction(address);
|
FunctionSymbol* fn = GetFunction(address);
|
||||||
if (fn) {
|
if (fn) {
|
||||||
|
if (opt_call_source) {
|
||||||
|
FunctionSymbol::AddCall(opt_call_source, fn);
|
||||||
|
}
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,6 +124,11 @@ FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
|
||||||
function_count_++;
|
function_count_++;
|
||||||
symbols_.insert(SymbolMap::value_type(address, fn));
|
symbols_.insert(SymbolMap::value_type(address, fn));
|
||||||
scan_queue_.push_back(fn);
|
scan_queue_.push_back(fn);
|
||||||
|
|
||||||
|
if (opt_call_source) {
|
||||||
|
FunctionSymbol::AddCall(opt_call_source, fn);
|
||||||
|
}
|
||||||
|
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -460,7 +469,9 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
// Function call.
|
// Function call.
|
||||||
block->outgoing_type = FunctionBlock::kTargetFunction;
|
block->outgoing_type = FunctionBlock::kTargetFunction;
|
||||||
block->outgoing_function = GetFunction(block->outgoing_address);
|
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",
|
XELOGE("call target not found: %.8X -> %.8X",
|
||||||
block->end_address, block->outgoing_address);
|
block->end_address, block->outgoing_address);
|
||||||
new_fns.push_back(block->outgoing_address);
|
new_fns.push_back(block->outgoing_address);
|
||||||
|
@ -474,7 +485,7 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
|
||||||
(uint32_t)new_fns.size());
|
(uint32_t)new_fns.size());
|
||||||
for (std::vector<uint32_t>::iterator it = new_fns.begin();
|
for (std::vector<uint32_t>::iterator it = new_fns.begin();
|
||||||
it != new_fns.end(); ++it) {
|
it != new_fns.end(); ++it) {
|
||||||
GetOrInsertFunction(*it);
|
GetOrInsertFunction(*it, fn);
|
||||||
}
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,8 @@ public:
|
||||||
|
|
||||||
Symbol* GetSymbol(uint32_t address);
|
Symbol* GetSymbol(uint32_t address);
|
||||||
ExceptionEntrySymbol* GetOrInsertExceptionEntry(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);
|
VariableSymbol* GetOrInsertVariable(uint32_t address);
|
||||||
FunctionSymbol* GetFunction(uint32_t address);
|
FunctionSymbol* GetFunction(uint32_t address);
|
||||||
VariableSymbol* GetVariable(uint32_t address);
|
VariableSymbol* GetVariable(uint32_t address);
|
||||||
|
|
|
@ -1,13 +1,20 @@
|
||||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'backend.h',
|
||||||
|
'code_unit_builder.h',
|
||||||
'cpu-private.h',
|
'cpu-private.h',
|
||||||
'cpu.cc',
|
'cpu.cc',
|
||||||
'cpu.h',
|
'cpu.h',
|
||||||
'exec_module.cc',
|
'exec_module.cc',
|
||||||
'exec_module.h',
|
'exec_module.h',
|
||||||
'llvm_exports.cc',
|
'function_table.cc',
|
||||||
'llvm_exports.h',
|
'function_table.h',
|
||||||
|
'global_exports.cc',
|
||||||
|
'global_exports.h',
|
||||||
|
'jit.h',
|
||||||
|
'library_linker.h',
|
||||||
|
'library_loader.h',
|
||||||
'ppc.h',
|
'ppc.h',
|
||||||
'processor.cc',
|
'processor.cc',
|
||||||
'processor.h',
|
'processor.h',
|
||||||
|
@ -16,7 +23,7 @@
|
||||||
],
|
],
|
||||||
|
|
||||||
'includes': [
|
'includes': [
|
||||||
'codegen/sources.gypi',
|
'llvmbe/sources.gypi',
|
||||||
'ppc/sources.gypi',
|
'ppc/sources.gypi',
|
||||||
'sdb/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:
|
private:
|
||||||
xe_memory_ref memory_;
|
xe_memory_ref memory_;
|
||||||
|
shared_ptr<Backend> backend_;
|
||||||
shared_ptr<Processor> processor_;
|
shared_ptr<Processor> processor_;
|
||||||
shared_ptr<Runtime> runtime_;
|
shared_ptr<Runtime> runtime_;
|
||||||
shared_ptr<Debugger> debugger_;
|
shared_ptr<Debugger> debugger_;
|
||||||
|
@ -45,6 +46,8 @@ int Run::Setup() {
|
||||||
xe_zero_struct(&pal_options, sizeof(pal_options));
|
xe_zero_struct(&pal_options, sizeof(pal_options));
|
||||||
XEEXPECTZERO(xe_pal_init(pal_options));
|
XEEXPECTZERO(xe_pal_init(pal_options));
|
||||||
|
|
||||||
|
backend_ = shared_ptr<Backend>(new xe::cpu::llvmbe::LLVMBackend());
|
||||||
|
|
||||||
debugger_ = shared_ptr<Debugger>(new Debugger());
|
debugger_ = shared_ptr<Debugger>(new Debugger());
|
||||||
|
|
||||||
xe_memory_options_t memory_options;
|
xe_memory_options_t memory_options;
|
||||||
|
@ -52,10 +55,11 @@ int Run::Setup() {
|
||||||
memory_ = xe_memory_create(memory_options);
|
memory_ = xe_memory_create(memory_options);
|
||||||
XEEXPECTNOTNULL(memory_);
|
XEEXPECTNOTNULL(memory_);
|
||||||
|
|
||||||
processor_ = shared_ptr<Processor>(new Processor(memory_));
|
processor_ = shared_ptr<Processor>(new Processor(memory_, backend_));
|
||||||
XEEXPECTZERO(processor_->Setup());
|
XEEXPECTZERO(processor_->Setup());
|
||||||
|
|
||||||
runtime_ = shared_ptr<Runtime>(new Runtime(processor_, XT("")));
|
runtime_ = shared_ptr<Runtime>(new Runtime(processor_, XT("")));
|
||||||
|
processor_->set_export_resolver(runtime_->export_resolver());
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
XECLEANUP:
|
XECLEANUP:
|
||||||
|
|
Loading…
Reference in New Issue