Broken, incomplete, but need to move forward with rewrite.

This commit is contained in:
Ben Vanik 2013-04-21 12:34:20 -07:00
parent 4987147055
commit b018b6fe56
51 changed files with 3455 additions and 2397 deletions

28
TODO.md
View File

@ -1,23 +1,13 @@
refactor:
module gen
recompiler (move to cpu)
implement:
function table
exec_module FindFunctionSymbol
ExecModule:
Prepare(memory, export_resolver)
Init()
Uninit()
Execute(ppc_state, addr)
JITExecModule:
LibraryExecModule:
Processor::Execute(addr):
// TODO: faster search, if needed
for each exec_module:
if (exec_module->Execute(addr)) {
break;
}
jit:
- add imports/exports/etc to module
- function generator (thread safe)

View File

@ -28,7 +28,7 @@ xe_pal_mac_t* pal;
void xe_pal_dealloc();
int xe_pal_init(xe_pal_options_t options) {
pal = (xe_pal_mac_t)xe_calloc(sizeof(pal));
pal = (xe_pal_mac_t*)xe_calloc(sizeof(pal));
mach_timebase_info_data_t info;
mach_timebase_info(&info);

48
src/xenia/cpu/backend.h Normal file
View File

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

View File

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

View File

@ -28,96 +28,6 @@
#include <xenia/cpu/ppc.h>
#include <xenia/cpu/codegen/function_generator.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::codegen;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
ModuleGenerator::ModuleGenerator(
xe_memory_ref memory, ExportResolver* export_resolver,
const char* module_name, const char* module_path, SymbolDatabase* sdb,
LLVMContext* context, Module* gen_module, ExecutionEngine* engine) {
memory_ = xe_memory_retain(memory);
export_resolver_ = export_resolver;
module_name_ = xestrdupa(module_name);
module_path_ = xestrdupa(module_path);
sdb_ = sdb;
context_ = context;
gen_module_ = gen_module;
engine_ = engine;
di_builder_ = NULL;
}
ModuleGenerator::~ModuleGenerator() {
for (std::map<uint32_t, CodegenFunction*>::iterator it =
functions_.begin(); it != functions_.end(); ++it) {
delete it->second;
}
delete di_builder_;
xe_free(module_path_);
xe_free(module_name_);
xe_memory_release(memory_);
}
int ModuleGenerator::Generate() {
std::string error_message;
// Setup a debug info builder.
// This is used when creating any debug info. We may want to go more
// fine grained than this, but for now it's something.
char dir[XE_MAX_PATH];
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path_));
char* slash = xestrrchra(dir, '/');
if (slash) {
*(slash + 1) = 0;
}
di_builder_ = new DIBuilder(*gen_module_);
di_builder_->createCompileUnit(
dwarf::DW_LANG_C99, //0x8010,
StringRef(module_name_),
StringRef(dir),
StringRef("xenia"),
true,
StringRef(""),
0);
cu_ = (MDNode*)di_builder_->getCU();
// Add export wrappers.
//
// Add all functions.
// We do two passes - the first creates the function signature and global
// value (so that we can call it), the second actually builds the function.
std::vector<FunctionSymbol*> functions;
if (!sdb_->GetAllFunctions(functions)) {
XELOGI("Beginning prep of %ld functions...", functions.size());
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
it != functions.end(); ++it) {
FunctionSymbol* fn = *it;
switch (fn->type) {
case FunctionSymbol::User:
PrepareFunction(fn);
break;
case FunctionSymbol::Kernel:
if (fn->kernel_export && fn->kernel_export->is_implemented) {
AddPresentImport(fn);
} else {
AddMissingImport(fn);
}
break;
default:
XEASSERTALWAYS();
break;
}
}
XELOGI("Function prep complete");
}
// Build out all the user functions.
size_t n = 0;
XELOGI("Beginning generation of %ld functions...", functions.size());
@ -143,55 +53,6 @@ void ModuleGenerator::AddFunctionsToMap(
}
}
ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
uint32_t address) {
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
if (it != functions_.end()) {
return it->second;
}
return NULL;
}
Function* ModuleGenerator::CreateFunctionDefinition(const char* name) {
Module* m = gen_module_;
LLVMContext& context = m->getContext();
std::vector<Type*> args;
args.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
args.push_back(Type::getInt64Ty(context));
Type* return_type = Type::getVoidTy(context);
FunctionType* ft = FunctionType::get(return_type,
ArrayRef<Type*>(args), false);
Function* f = cast<Function>(m->getOrInsertFunction(
StringRef(name), ft));
f->setVisibility(GlobalValue::DefaultVisibility);
// Indicate that the function will never be unwound with an exception.
// If we ever support native exception handling we may need to remove this.
f->doesNotThrow();
// May be worth trying the X86_FastCall, as we only need state in a register.
//f->setCallingConv(CallingConv::Fast);
f->setCallingConv(CallingConv::C);
Function::arg_iterator fn_args = f->arg_begin();
// 'state'
Value* fn_arg = fn_args++;
fn_arg->setName("state");
f->setDoesNotAlias(1);
f->setDoesNotCapture(1);
// 'state' should try to be in a register, if possible.
// TODO(benvanik): verify that's a good idea.
// f->getArgumentList().begin()->addAttr(
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
// 'lr'
fn_arg = fn_args++;
fn_arg->setName("lr");
return f;
};
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
Module *m = gen_module_;
@ -288,48 +149,3 @@ void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
OptimizeFunction(m, f);
}
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name());
// Setup our codegen wrapper to keep all the pointers together.
CodegenFunction* cgf = new CodegenFunction();
cgf->symbol = fn;
cgf->function_type = f->getFunctionType();
cgf->function = f;
functions_.insert(std::pair<uint32_t, CodegenFunction*>(
fn->start_address, cgf));
}
void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
FunctionSymbol* fn = cgf->symbol;
// Setup the generation context.
FunctionGenerator fgen(
memory_, sdb_, fn, context_, gen_module_, cgf->function);
// Run through and generate each basic block.
fgen.GenerateBasicBlocks();
// Run the optimizer on the function.
// Doing this here keeps the size of the IR small and speeds up the later
// passes.
OptimizeFunction(gen_module_, cgf->function);
}
void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
FunctionPassManager pm(m);
//fn->dump();
if (FLAGS_optimize_ir_functions) {
PassManagerBuilder pmb;
pmb.OptLevel = 3;
pmb.SizeLevel = 0;
pmb.Inliner = createFunctionInliningPass();
pmb.Vectorize = true;
pmb.LoopVectorize = true;
pmb.populateFunctionPassManager(pm);
}
pm.add(createVerifierPass());
pm.run(*fn);
}

View File

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

View File

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

View File

@ -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',
],
}

View File

@ -12,4 +12,7 @@
#include <xenia/cpu/processor.h>
// TODO(benvanik): conditionally include?
#include <xenia/cpu/llvmbe/llvm_backend.h>
#endif // XENIA_CPU_CPU_H_

View File

@ -9,67 +9,49 @@
#include <xenia/cpu/exec_module.h>
#include <llvm/Linker.h>
#include <llvm/PassManager.h>
#include <llvm/Analysis/Verifier.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/DataLayout.h>
#include <llvm/IR/DerivedTypes.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/Host.h>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/system_error.h>
#include <llvm/Support/Threading.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <xenia/cpu/cpu-private.h>
#include <xenia/cpu/llvm_exports.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/codegen/module_generator.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
#include <xenia/cpu/xethunk/xethunk.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::codegen;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
ExecModule::ExecModule(
xe_memory_ref memory, shared_ptr<ExportResolver> export_resolver,
const char* module_name, const char* module_path,
shared_ptr<llvm::ExecutionEngine>& engine) {
FunctionTable* fn_table,
const char* module_name, const char* module_path) {
memory_ = xe_memory_retain(memory);
export_resolver_ = export_resolver;
fn_table_ = fn_table;
module_name_ = xestrdupa(module_name);
module_path_ = xestrdupa(module_path);
engine_ = engine;
context_ = shared_ptr<LLVMContext>(new LLVMContext());
}
ExecModule::~ExecModule() {
if (gen_module_) {
Uninit();
engine_->removeModule(gen_module_.get());
}
xe_free(module_path_);
xe_free(module_name_);
xe_memory_release(memory_);
}
int ExecModule::PrepareXex(xe_xex2_ref xex) {
SymbolDatabase* ExecModule::sdb() {
return sdb_.get();
}
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
start_address, end_address));
code_addr_low_ = start_address;
code_addr_high_ = end_address;
return Prepare();
}
int ExecModule::PrepareXexModule(xe_xex2_ref xex) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), xex));
@ -94,64 +76,19 @@ int ExecModule::PrepareXex(xe_xex2_ref xex) {
return result_code;
}
// Import variables.
// TODO??
return 0;
}
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::RawSymbolDatabase(memory_, export_resolver_.get(),
start_address, end_address));
code_addr_low_ = start_address;
code_addr_high_ = end_address;
return Prepare();
}
int ExecModule::Prepare() {
int result_code = 1;
std::string error_message;
char file_name[XE_MAX_PATH];
OwningPtr<MemoryBuffer> shared_module_buffer;
auto_ptr<Module> shared_module;
auto_ptr<raw_ostream> outs;
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.
// 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());
// Load a specified module map and diff.
@ -166,124 +103,18 @@ int ExecModule::Prepare() {
sdb_->WriteMap(file_name);
}
// Initialize the module.
gen_module_ = shared_ptr<Module>(
new Module(module_name_, *context_.get()));
// TODO(benavnik): addModuleFlag?
// Inject globals.
// This should be done ASAP to ensure that JITed functions can use the
// constant addresses.
XEEXPECTZERO(InjectGlobals());
// Link shared module into generated module.
// This gives us a single module that we can optimize and prevents the need
// for foreward declarations.
Linker::LinkModules(gen_module_.get(), shared_module.get(), 0,
&error_message);
// Build the module from the source code.
codegen_ = auto_ptr<ModuleGenerator>(new ModuleGenerator(
memory_, export_resolver_.get(), module_name_, module_path_,
sdb_.get(), context_.get(), gen_module_.get(),
engine_.get()));
XEEXPECTZERO(codegen_->Generate());
// Write to cache.
// TODO(benvanik): cache stuff
// Dump pre-optimized module to disk.
if (FLAGS_dump_module_bitcode) {
xesnprintfa(file_name, XECOUNT(file_name),
"%s%s-preopt.bc", FLAGS_dump_path.c_str(), module_name_);
outs = auto_ptr<raw_ostream>(new raw_fd_ostream(
file_name, error_message, raw_fd_ostream::F_Binary));
XEEXPECTTRUE(error_message.empty());
WriteBitcodeToFile(gen_module_.get(), *outs);
}
}
// 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.
// If recompiling, setup a recompiler and run.
// Note that this will just load the library if it's present and valid.
// TODO(benvanik): recompiler logic.
// Initialize the module.
XEEXPECTZERO(Init());
// Force JIT of all functions.
// for (Module::iterator it = gen_module_->begin(); it != gen_module_->end();
// ++it) {
// Function* fn = it;
// if (!fn->isDeclaration()) {
// engine_->getPointerToFunction(fn);
// }
// }
result_code = 0;
XECLEANUP:
return result_code;
}
void ExecModule::AddFunctionsToMap(FunctionMap& map) {
codegen_->AddFunctionsToMap(map);
}
int ExecModule::InjectGlobals() {
LLVMContext& context = *context_.get();
const DataLayout* dl = engine_->getDataLayout();
Type* intPtrTy = dl->getIntPtrType(context);
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
GlobalVariable* gv;
// xe_memory_base
// This is the base void* pointer to the memory space.
gv = new GlobalVariable(
*gen_module_,
int8PtrTy,
true,
GlobalValue::ExternalLinkage,
0,
"xe_memory_base");
// Align to 64b - this makes SSE faster.
gv->setAlignment(64);
gv->setInitializer(ConstantExpr::getIntToPtr(
ConstantInt::get(intPtrTy, (uintptr_t)xe_memory_addr(memory_, 0)),
int8PtrTy));
SetupLlvmExports(gen_module_.get(), dl, engine_.get());
return 0;
}
int ExecModule::Init() {
// Setup all kernel variables.
std::vector<VariableSymbol*> variables;
@ -317,27 +148,11 @@ int ExecModule::Init() {
}
}
// Run static initializers. I'm not sure we'll have any, but who knows.
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);
// Grab the init function and call it.
Function* xe_module_init = gen_module_->getFunction("xe_module_init");
std::vector<GenericValue> args;
GenericValue ret = engine_->runFunction(xe_module_init, args);
return static_cast<int>(ret.IntVal.getSExtValue());
return 0;
}
int ExecModule::Uninit() {
// Grab function and call it.
Function* xe_module_uninit = gen_module_->getFunction("xe_module_uninit");
std::vector<GenericValue> args;
engine_->runFunction(xe_module_uninit, args);
// Run static destructors.
engine_->runStaticConstructorsDestructors(gen_module_.get(), true);
return 0;
FunctionSymbol* ExecModule::FindFunctionSymbol(uint32_t address) {
return sdb_->GetFunction(address);
}
void ExecModule::Dump() {

View File

@ -13,68 +13,46 @@
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/cpu/function_table.h>
#include <xenia/cpu/sdb.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
class ExecutionEngine;
class Function;
class LLVMContext;
class Module;
}
namespace xe {
namespace cpu {
namespace codegen {
class ModuleGenerator;
}
}
}
namespace xe {
namespace cpu {
typedef std::tr1::unordered_map<uint32_t, llvm::Function*> FunctionMap;
class ExecModule {
public:
ExecModule(
xe_memory_ref memory, shared_ptr<kernel::ExportResolver> export_resolver,
const char* module_name, const char* module_path,
shared_ptr<llvm::ExecutionEngine>& engine);
FunctionTable* fn_table,
const char* module_name, const char* module_path);
~ExecModule();
int PrepareXex(xe_xex2_ref xex);
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
sdb::SymbolDatabase* sdb();
void AddFunctionsToMap(FunctionMap& map);
int PrepareRawBinary(uint32_t start_address, uint32_t end_address);
int PrepareXexModule(xe_xex2_ref xex);
sdb::FunctionSymbol* FindFunctionSymbol(uint32_t address);
void Dump();
private:
int Prepare();
int InjectGlobals();
int Init();
int Uninit();
xe_memory_ref memory_;
shared_ptr<kernel::ExportResolver> export_resolver_;
FunctionTable* fn_table_;
char* module_name_;
char* module_path_;
shared_ptr<llvm::ExecutionEngine> engine_;
shared_ptr<sdb::SymbolDatabase> sdb_;
shared_ptr<llvm::LLVMContext> context_;
shared_ptr<llvm::Module> gen_module_;
auto_ptr<codegen::ModuleGenerator> codegen_;
shared_ptr<sdb::SymbolDatabase> sdb_;
uint32_t code_addr_low_;
uint32_t code_addr_high_;
FunctionMap fns_;
};

View File

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

View File

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

View File

@ -0,0 +1,105 @@
/**
******************************************************************************
* Xenia : Xbox 360 Emulator Research Project *
******************************************************************************
* Copyright 2013 Ben Vanik. All rights reserved. *
* Released under the BSD license - see LICENSE in the root for more details. *
******************************************************************************
*/
#include <xenia/cpu/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;
}

View File

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

55
src/xenia/cpu/jit.h Normal file
View File

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

View File

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

View File

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

View File

@ -7,15 +7,15 @@
******************************************************************************
*/
#ifndef XENIA_CPU_CODEGEN_EMIT_H_
#define XENIA_CPU_CODEGEN_EMIT_H_
#ifndef XENIA_CPU_LLVMBE_EMIT_H_
#define XENIA_CPU_LLVMBE_EMIT_H_
#include <xenia/cpu/ppc/instr.h>
namespace xe {
namespace cpu {
namespace codegen {
namespace llvmbe {
void RegisterEmitCategoryALU();
@ -33,9 +33,9 @@ void RegisterEmitCategoryMemory();
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
} // namespace codegen
} // namespace llvmbe
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_CODEGEN_EMIT_H_
#endif // XENIA_CPU_LLVMBE_EMIT_H_

View File

@ -7,25 +7,25 @@
******************************************************************************
*/
#include <xenia/cpu/codegen/emit.h>
#include <xenia/cpu/llvmbe/emit.h>
#include <xenia/cpu/codegen/function_generator.h>
#include <xenia/cpu/llvmbe/emitter_context.h>
#include <xenia/cpu/ppc/state.h>
using namespace llvm;
using namespace xe::cpu::codegen;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
namespace xe {
namespace cpu {
namespace codegen {
namespace llvmbe {
int XeEmitIndirectBranchTo(
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
bool lk, uint32_t reg) {
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
// a normal function return that is pulling the LR from the stack that
@ -37,10 +37,10 @@ int XeEmitIndirectBranchTo(
Value* target;
switch (reg) {
case kXEPPCRegLR:
target = g.lr_value();
target = e.lr_value();
break;
case kXEPPCRegCTR:
target = g.ctr_value();
target = e.ctr_value();
break;
default:
XEASSERTALWAYS();
@ -52,29 +52,29 @@ int XeEmitIndirectBranchTo(
// Ideally it's a return and we can just do a simple ret and be done.
// If it's not, we fall through to the full indirection logic.
if (!lk && reg == kXEPPCRegLR) {
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* mismatch_bb = BasicBlock::Create(*g.context(), "lr_mismatch",
g.gen_fn(), next_block);
Value* lr_cmp = b.CreateICmpEQ(target, ++(g.gen_fn()->arg_begin()));
BasicBlock* next_block = e.GetNextBasicBlock();
BasicBlock* mismatch_bb = BasicBlock::Create(*e.context(), "lr_mismatch",
e.gen_fn(), next_block);
Value* lr_cmp = b.CreateICmpEQ(target, ++(e.gen_fn()->arg_begin()));
// The return block will spill registers for us.
b.CreateCondBr(lr_cmp, g.GetReturnBasicBlock(), mismatch_bb);
b.CreateCondBr(lr_cmp, e.GetReturnBasicBlock(), mismatch_bb);
b.SetInsertPoint(mismatch_bb);
}
// Defer to the generator, which will do fancy things.
bool likely_local = !lk && reg == kXEPPCRegCTR;
return g.GenerateIndirectionBranch(cia, target, lk, likely_local);
return e.GenerateIndirectionBranch(cia, target, lk, likely_local);
}
int XeEmitBranchTo(
FunctionGenerator& g, IRBuilder<>& b, const char* src, uint32_t cia,
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
bool lk) {
// Get the basic block and switch behavior based on outgoing type.
FunctionBlock* fn_block = g.fn_block();
FunctionBlock* fn_block = e.fn_block();
switch (fn_block->outgoing_type) {
case FunctionBlock::kTargetBlock:
{
BasicBlock* target_bb = g.GetBasicBlock(fn_block->outgoing_address);
BasicBlock* target_bb = e.GetBasicBlock(fn_block->outgoing_address);
XEASSERTNOTNULL(target_bb);
b.CreateBr(target_bb);
break;
@ -85,13 +85,13 @@ int XeEmitBranchTo(
// TODO(benvanik): only spill ones used by the target function? Use
// calling convention flags on the function to not spill temp
// registers?
g.SpillRegisters();
e.SpillRegisters();
XEASSERTNOTNULL(fn_block->outgoing_function);
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
Function::arg_iterator args = g.gen_fn()->arg_begin();
Function* target_fn = e.GetFunction(fn_block->outgoing_function);
Function::arg_iterator args = e.gen_fn()->arg_begin();
Value* state_ptr = args;
BasicBlock* next_bb = g.GetNextBasicBlock();
BasicBlock* next_bb = e.GetNextBasicBlock();
if (!lk || !next_bb) {
// Tail. No need to refill the local register values, just return.
// We optimize this by passing in the LR from our parent instead of the
@ -103,7 +103,7 @@ int XeEmitBranchTo(
// Will return here eventually.
// Refill registers from state.
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
g.FillRegisters();
e.FillRegisters();
b.CreateBr(next_bb);
}
break;
@ -112,13 +112,13 @@ int XeEmitBranchTo(
{
// An indirect jump.
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegLR);
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegLR);
}
case FunctionBlock::kTargetCTR:
{
// An indirect jump.
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
return XeEmitIndirectBranchTo(g, b, src, cia, lk, kXEPPCRegCTR);
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegCTR);
}
default:
case FunctionBlock::kTargetNone:
@ -129,7 +129,7 @@ int XeEmitBranchTo(
}
XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(bx, 0x48000000, I )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// if AA then
// NIA <- EXTS(LI || 0b00)
// else
@ -144,13 +144,13 @@ XEEMITTER(bx, 0x48000000, I )(FunctionGenerator& g, IRBuilder<>& b, I
nia = i.address + XEEXTS26(i.I.LI << 2);
}
if (i.I.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
e.update_lr_value(b.getInt32(i.address + 4));
}
return XeEmitBranchTo(g, b, "bx", i.address, i.I.LK);
return XeEmitBranchTo(e, b, "bx", i.address, i.I.LK);
}
XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(bcx, 0x40000000, B )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// if ¬BO[2] then
// CTR <- CTR - 1
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
@ -170,7 +170,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.B.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
e.update_lr_value(b.getInt32(i.address + 4));
}
Value* ctr_ok = NULL;
@ -178,9 +178,9 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
// Ignore ctr.
} else {
// Decrement counter.
Value* ctr = g.ctr_value();
Value* ctr = e.ctr_value();
ctr = b.CreateSub(ctr, b.getInt64(1));
g.update_ctr_value(ctr);
e.update_ctr_value(ctr);
// Ctr check.
if (XESELECTBITS(i.B.BO, 1, 1)) {
@ -194,7 +194,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
if (XESELECTBITS(i.B.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.B.BI >> 2);
Value* cr = e.cr_value(i.B.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.B.BI & 3));
if (XESELECTBITS(i.B.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
@ -218,8 +218,8 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
BasicBlock* next_block = e.GetNextBasicBlock();
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
@ -233,7 +233,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
} else {
nia = i.address + XEEXTS26(i.B.BD << 2);
}
if (XeEmitBranchTo(g, b, "bcx", i.address, i.B.LK)) {
if (XeEmitBranchTo(e, b, "bcx", i.address, i.B.LK)) {
return 1;
}
@ -242,7 +242,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
return 0;
}
XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(bcctrx, 0x4C000420, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
// if cond_ok then
// NIA <- CTR[0:61] || 0b00
@ -256,14 +256,14 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.XL.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
e.update_lr_value(b.getInt32(i.address + 4));
}
Value* cond_ok = NULL;
if (XESELECTBITS(i.XL.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
Value* cr = e.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
if (XESELECTBITS(i.XL.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
@ -283,9 +283,9 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* next_block = e.GetNextBasicBlock();
XEASSERTNOTNULL(next_block);
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
@ -293,7 +293,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
}
// Note that this occurs entirely within the branch true block.
if (XeEmitBranchTo(g, b, "bcctrx", i.address, i.XL.LK)) {
if (XeEmitBranchTo(e, b, "bcctrx", i.address, i.XL.LK)) {
return 1;
}
@ -302,7 +302,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
return 0;
}
XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(bclrx, 0x4C000020, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// if ¬BO[2] then
// CTR <- CTR - 1
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
@ -319,7 +319,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
// The docs say always, though...
if (i.XL.LK) {
g.update_lr_value(b.getInt32(i.address + 4));
e.update_lr_value(b.getInt32(i.address + 4));
}
Value* ctr_ok = NULL;
@ -327,7 +327,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// Ignore ctr.
} else {
// Decrement counter.
Value* ctr = g.ctr_value();
Value* ctr = e.ctr_value();
ctr = b.CreateSub(ctr, b.getInt64(1));
// Ctr check.
@ -342,7 +342,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
if (XESELECTBITS(i.XL.BO, 4, 4)) {
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
Value* cr = e.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (i.XL.BI & 3));
if (XESELECTBITS(i.XL.BO, 3, 3)) {
cond_ok = b.CreateICmpNE(cr, b.getInt64(0));
@ -366,9 +366,9 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
if (ok) {
char name[32];
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
BasicBlock* next_block = g.GetNextBasicBlock();
BasicBlock* next_block = e.GetNextBasicBlock();
XEASSERTNOTNULL(next_block);
BasicBlock* branch_bb = BasicBlock::Create(*g.context(), name, g.gen_fn(),
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
next_block);
b.CreateCondBr(ok, branch_bb, next_block);
@ -376,7 +376,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
}
// Note that this occurs entirely within the branch true block.
if (XeEmitBranchTo(g, b, "bclrx", i.address, i.XL.LK)) {
if (XeEmitBranchTo(e, b, "bclrx", i.address, i.XL.LK)) {
return 1;
}
@ -388,47 +388,47 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// Condition register logical (A-23)
XEEMITTER(crand, 0x4C000202, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crand, 0x4C000202, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crandc, 0x4C000102, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crandc, 0x4C000102, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(creqv, 0x4C000242, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(creqv, 0x4C000242, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crnand, 0x4C0001C2, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crnand, 0x4C0001C2, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crnor, 0x4C000042, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crnor, 0x4C000042, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(cror, 0x4C000382, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(cror, 0x4C000382, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crorc, 0x4C000342, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crorc, 0x4C000342, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(crxor, 0x4C000182, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(crxor, 0x4C000182, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mcrf, 0x4C000000, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -436,7 +436,7 @@ XEEMITTER(mcrf, 0x4C000000, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// System linkage (A-24)
XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(sc, 0x44000002, SC )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -444,7 +444,7 @@ XEEMITTER(sc, 0x44000002, SC )(FunctionGenerator& g, IRBuilder<>& b, I
// Trap (A-25)
int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
int XeEmitTrap(EmitterContext& e, IRBuilder<>& b, InstrData& i,
Value* va, Value* vb, uint32_t TO) {
// if (a < b) & TO[0] then TRAP
// if (a > b) & TO[1] then TRAP
@ -459,27 +459,27 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
return 0;
}
BasicBlock* after_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
g.GetNextBasicBlock());
BasicBlock* trap_bb = BasicBlock::Create(*g.context(), "", g.gen_fn(),
BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
e.GetNextBasicBlock());
BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
after_bb);
// Create the basic blocks (so we can chain).
std::vector<BasicBlock*> bbs;
if (TO & (1 << 4)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
}
if (TO & (1 << 3)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
}
if (TO & (1 << 2)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
}
if (TO & (1 << 1)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
}
if (TO & (1 << 0)) {
bbs.push_back(BasicBlock::Create(*g.context(), "", g.gen_fn(), trap_bb));
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
}
bbs.push_back(after_bb);
@ -526,10 +526,10 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
// Create trap BB.
b.SetInsertPoint(trap_bb);
g.SpillRegisters();
e.SpillRegisters();
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
b.CreateCall2(g.gen_module()->getFunction("XeTrap"),
g.gen_fn()->arg_begin(),
b.CreateCall2(e.gen_module()->getFunction("XeTrap"),
e.gen_fn()->arg_begin(),
b.getInt32(i.address));
b.CreateBr(after_bb);
@ -539,7 +539,7 @@ int XeEmitTrap(FunctionGenerator& g, IRBuilder<>& b, InstrData& i,
return 0;
}
XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(td, 0x7C000088, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// a <- (RA)
// b <- (RB)
// if (a < b) & TO[0] then TRAP
@ -547,26 +547,26 @@ XEEMITTER(td, 0x7C000088, X )(FunctionGenerator& g, IRBuilder<>& b, I
// if (a = b) & TO[2] then TRAP
// if (a <u b) & TO[3] then TRAP
// if (a >u b) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
g.gpr_value(i.X.RA),
g.gpr_value(i.X.RB),
return XeEmitTrap(e, b, i,
e.gpr_value(i.X.RA),
e.gpr_value(i.X.RB),
i.X.RT);
}
XEEMITTER(tdi, 0x08000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(tdi, 0x08000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// a <- (RA)
// if (a < EXTS(SI)) & TO[0] then TRAP
// if (a > EXTS(SI)) & TO[1] then TRAP
// if (a = EXTS(SI)) & TO[2] then TRAP
// if (a <u EXTS(SI)) & TO[3] then TRAP
// if (a >u EXTS(SI)) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
g.gpr_value(i.D.RA),
return XeEmitTrap(e, b, i,
e.gpr_value(i.D.RA),
b.getInt64(XEEXTS16(i.D.DS)),
i.D.RT);
}
XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(tw, 0x7C000008, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// a <- EXTS((RA)[32:63])
// b <- EXTS((RB)[32:63])
// if (a < b) & TO[0] then TRAP
@ -574,25 +574,25 @@ XEEMITTER(tw, 0x7C000008, X )(FunctionGenerator& g, IRBuilder<>& b, I
// if (a = b) & TO[2] then TRAP
// if (a <u b) & TO[3] then TRAP
// if (a >u b) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RA),
return XeEmitTrap(e, b, i,
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RA),
b.getInt32Ty()),
b.getInt64Ty()),
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.X.RB),
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RB),
b.getInt32Ty()),
b.getInt64Ty()),
i.X.RT);
}
XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(twi, 0x0C000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// a <- EXTS((RA)[32:63])
// if (a < EXTS(SI)) & TO[0] then TRAP
// if (a > EXTS(SI)) & TO[1] then TRAP
// if (a = EXTS(SI)) & TO[2] then TRAP
// if (a <u EXTS(SI)) & TO[3] then TRAP
// if (a >u EXTS(SI)) & TO[4] then TRAP
return XeEmitTrap(g, b, i,
b.CreateSExt(b.CreateTrunc(g.gpr_value(i.D.RA),
return XeEmitTrap(e, b, i,
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.D.RA),
b.getInt32Ty()),
b.getInt64Ty()),
b.getInt64(XEEXTS16(i.D.DS)),
@ -602,12 +602,12 @@ XEEMITTER(twi, 0x0C000000, D )(FunctionGenerator& g, IRBuilder<>& b, I
// Processor control (A-26)
XEEMITTER(mfcr, 0x7C000026, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mfcr, 0x7C000026, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mfspr, 0x7C0002A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// n <- spr[5:9] || spr[0:4]
// if length(SPR(n)) = 64 then
// RT <- SPR(n)
@ -619,58 +619,58 @@ XEEMITTER(mfspr, 0x7C0002A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, I
switch (n) {
case 1:
// XER
v = g.xer_value();
v = e.xer_value();
break;
case 8:
// LR
v = g.lr_value();
v = e.lr_value();
break;
case 9:
// CTR
v = g.ctr_value();
v = e.ctr_value();
break;
default:
XEINSTRNOTIMPLEMENTED();
return 1;
}
g.update_gpr_value(i.XFX.RT, v);
e.update_gpr_value(i.XFX.RT, v);
return 0;
}
XEEMITTER(mftb, 0x7C0002E6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mftb, 0x7C0002E6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtcrf, 0x7C000120, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtcrf, 0x7C000120, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtspr, 0x7C0003A6, XFX)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtspr, 0x7C0003A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// n <- spr[5:9] || spr[0:4]
// if length(SPR(n)) = 64 then
// SPR(n) <- (RS)
// else
// SPR(n) <- (RS)[32:63]
Value* v = g.gpr_value(i.XFX.RT);
Value* v = e.gpr_value(i.XFX.RT);
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
switch (n) {
case 1:
// XER
g.update_xer_value(v);
e.update_xer_value(v);
break;
case 8:
// LR
g.update_lr_value(v);
e.update_lr_value(v);
break;
case 9:
// CTR
g.update_ctr_value(v);
e.update_ctr_value(v);
break;
default:
XEINSTRNOTIMPLEMENTED();
@ -708,6 +708,6 @@ void RegisterEmitCategoryControl() {
}
} // namespace codegen
} // namespace llvmbe
} // namespace cpu
} // namespace xe

View File

@ -7,84 +7,84 @@
******************************************************************************
*/
#include <xenia/cpu/codegen/emit.h>
#include <xenia/cpu/llvmbe/emit.h>
#include <xenia/cpu/codegen/function_generator.h>
#include <xenia/cpu/llvmbe/emitter_context.h>
using namespace llvm;
using namespace xe::cpu::codegen;
using namespace xe::cpu;
using namespace xe::cpu::ppc;
namespace xe {
namespace cpu {
namespace codegen {
namespace llvmbe {
// Floating-point arithmetic (A-8)
XEEMITTER(faddx, 0xFC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(faddx, 0xFC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(faddsx, 0xEC00002A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(faddsx, 0xEC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fdivx, 0xFC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fdivx, 0xFC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fdivsx, 0xEC000024, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fdivsx, 0xEC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmulx, 0xFC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmulx, 0xFC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmulsx, 0xEC000032, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmulsx, 0xEC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fresx, 0xEC000030, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fresx, 0xEC000030, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(frsqrtex, 0xFC000034, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(frsqrtex, 0xFC000034, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsubx, 0xFC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fsubx, 0xFC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsubsx, 0xEC000028, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fsubsx, 0xEC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fselx, 0xFC00002E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fselx, 0xFC00002E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsqrtx, 0xFC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fsqrtx, 0xFC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fsqrtsx, 0xEC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -92,42 +92,42 @@ XEEMITTER(fsqrtsx, 0xEC00002C, A )(FunctionGenerator& g, IRBuilder<>& b, I
// Floating-point multiply-add (A-9)
XEEMITTER(fmaddx, 0xFC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmaddx, 0xFC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmaddsx, 0xEC00003A, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmaddsx, 0xEC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmsubx, 0xFC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmsubx, 0xFC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmsubsx, 0xEC000038, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmsubsx, 0xEC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmaddx, 0xFC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnmaddx, 0xFC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmaddsx, 0xEC00003E, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnmaddsx, 0xEC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmsubx, 0xFC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnmsubx, 0xFC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnmsubsx, 0xEC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -135,32 +135,32 @@ XEEMITTER(fnmsubsx, 0xEC00003C, A )(FunctionGenerator& g, IRBuilder<>& b, I
// Floating-point rounding and conversion (A-10)
XEEMITTER(fcfidx, 0xFC00069C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fcfidx, 0xFC00069C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctidx, 0xFC00065C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fctidx, 0xFC00065C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctidzx, 0xFC00065E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fctidzx, 0xFC00065E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctiwx, 0xFC00001C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fctiwx, 0xFC00001C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fctiwzx, 0xFC00001E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fctiwzx, 0xFC00001E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(frspx, 0xFC000018, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -168,12 +168,12 @@ XEEMITTER(frspx, 0xFC000018, X )(FunctionGenerator& g, IRBuilder<>& b, I
// Floating-point compare (A-11)
XEEMITTER(fcmpo, 0xFC000040, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fcmpo, 0xFC000040, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fcmpu, 0xFC000000, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
// if (FRA) is a NaN or (FRB) is a NaN then
// c <- 0b0001
// else if (FRA) < (FRB) then
@ -194,32 +194,32 @@ XEEMITTER(fcmpu, 0xFC000000, X )(FunctionGenerator& g, IRBuilder<>& b, I
// Floating-point status and control register (A
XEEMITTER(mcrfs, 0xFC000080, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mcrfs, 0xFC000080, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mffsx, 0xFC00048E, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mffsx, 0xFC00048E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsb0x, 0xFC00008C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtfsb0x, 0xFC00008C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsb1x, 0xFC00004C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtfsb1x, 0xFC00004C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(mtfsfix, 0xFC00010C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -227,22 +227,22 @@ XEEMITTER(mtfsfix, 0xFC00010C, X )(FunctionGenerator& g, IRBuilder<>& b, I
// Floating-point move (A-21)
XEEMITTER(fabsx, 0xFC000210, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fabsx, 0xFC000210, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fmrx, 0xFC000090, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fmrx, 0xFC000090, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnabsx, 0xFC000110, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnabsx, 0xFC000110, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
XEEMITTER(fnegx, 0xFC000050, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
XEEMITTER(fnegx, 0xFC000050, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
XEINSTRNOTIMPLEMENTED();
return 1;
}
@ -291,6 +291,6 @@ void RegisterEmitCategoryFPU() {
}
} // namespace codegen
} // namespace llvmbe
} // namespace cpu
} // namespace xe

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@
******************************************************************************
*/
#include <xenia/cpu/codegen/function_generator.h>
#include <xenia/cpu/llvmbe/emitter_context.h>
#include <llvm/IR/Intrinsics.h>
@ -16,7 +16,7 @@
using namespace llvm;
using namespace xe::cpu::codegen;
using namespace xe::cpu::llvmbe;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
@ -29,8 +29,9 @@ DEFINE_bool(log_codegen, false,
/**
* This generates function code.
* One context is created for each function to generate. Each basic block in
* the function is created and stashed in one pass, then filled in the next.
* One context is created and shared for each function to generate.
* Each basic block in the function is created and stashed in one pass, then
* filled in the next.
*
* This context object is a stateful representation of the current machine state
* and all accessors to registers should occur through it. By doing so it's
@ -46,22 +47,44 @@ DEFINE_bool(log_codegen, false,
*/
FunctionGenerator::FunctionGenerator(
xe_memory_ref memory, SymbolDatabase* sdb, FunctionSymbol* fn,
LLVMContext* context, Module* gen_module, Function* gen_fn) {
EmitterContext::EmitterContext(
xe_memory_ref memory,
LLVMContext* context, Module* gen_module) {
memory_ = memory;
sdb_ = sdb;
fn_ = fn;
context_ = context;
gen_module_ = gen_module;
gen_fn_ = gen_fn;
builder_ = new IRBuilder<>(*context_);
// Function type for all functions.
std::vector<Type*> args;
args.push_back(PointerType::getUnqual(Type::getInt8Ty(*context)));
args.push_back(Type::getInt64Ty(*context));
Type* return_type = Type::getVoidTy(*context);
fn_type_ = FunctionType::get(
return_type, ArrayRef<Type*>(args), false);
}
EmitterContext::~EmitterContext() {
delete builder_;
}
int EmitterContext::Init(FunctionSymbol* fn, Function* gen_fn) {
builder_->ClearInsertionPoint();
fn_ = fn;
gen_fn_ = gen_fn;
fn_block_ = NULL;
return_block_ = NULL;
internal_indirection_block_ = NULL;
external_indirection_block_ = NULL;
bb_ = NULL;
insert_points_.clear();
bbs_.clear();
cia_ = 0;
access_bits_.Clear();
locals_.indirection_target = NULL;
@ -80,53 +103,49 @@ FunctionGenerator::FunctionGenerator(
locals_.fpr[n] = NULL;
}
if (fn) {
if (FLAGS_log_codegen) {
printf("%s:\n", fn->name());
}
}
return 0;
}
FunctionGenerator::~FunctionGenerator() {
delete builder_;
}
SymbolDatabase* FunctionGenerator::sdb() {
return sdb_;
}
FunctionSymbol* FunctionGenerator::fn() {
return fn_;
}
llvm::LLVMContext* FunctionGenerator::context() {
llvm::LLVMContext* EmitterContext::context() {
return context_;
}
llvm::Module* FunctionGenerator::gen_module() {
llvm::Module* EmitterContext::gen_module() {
return gen_module_;
}
llvm::Function* FunctionGenerator::gen_fn() {
FunctionSymbol* EmitterContext::fn() {
return fn_;
}
llvm::Function* EmitterContext::gen_fn() {
return gen_fn_;
}
FunctionBlock* FunctionGenerator::fn_block() {
FunctionBlock* EmitterContext::fn_block() {
return fn_block_;
}
void FunctionGenerator::PushInsertPoint() {
void EmitterContext::PushInsertPoint() {
IRBuilder<>& b = *builder_;
insert_points_.push_back(std::pair<BasicBlock*, BasicBlock::iterator>(
b.GetInsertBlock(), b.GetInsertPoint()));
}
void FunctionGenerator::PopInsertPoint() {
void EmitterContext::PopInsertPoint() {
IRBuilder<>& b = *builder_;
std::pair<BasicBlock*, BasicBlock::iterator> back = insert_points_.back();
b.SetInsertPoint(back.first, back.second);
insert_points_.pop_back();
}
void FunctionGenerator::GenerateBasicBlocks() {
void EmitterContext::GenerateBasicBlocks() {
IRBuilder<>& b = *builder_;
// Always add an entry block.
@ -178,7 +197,7 @@ void FunctionGenerator::GenerateBasicBlocks() {
GenerateSharedBlocks();
}
void FunctionGenerator::GenerateSharedBlocks() {
void EmitterContext::GenerateSharedBlocks() {
IRBuilder<>& b = *builder_;
Value* indirect_branch = gen_module_->getFunction("XeIndirectBranch");
@ -225,7 +244,7 @@ void FunctionGenerator::GenerateSharedBlocks() {
}
}
int FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
int EmitterContext::PrepareBasicBlock(FunctionBlock* block) {
// Create the basic block that will end up getting filled during
// generation.
char name[32];
@ -271,7 +290,7 @@ int FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
return 0;
}
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
void EmitterContext::GenerateBasicBlock(FunctionBlock* block) {
IRBuilder<>& b = *builder_;
BasicBlock* bb = GetBasicBlock(block->start_address);
@ -337,7 +356,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
// builder_>SetCurrentDebugLocation(DebugLoc::get(
// ia >> 8, ia & 0xFF, ctx->cu));
typedef int (*InstrEmitter)(FunctionGenerator& g, IRBuilder<>& b,
typedef int (*InstrEmitter)(EmitterContext& g, IRBuilder<>& b,
InstrData& i);
InstrEmitter emit = (InstrEmitter)i.type->emit;
if (!i.type->emit || emit(*this, *builder_, i)) {
@ -371,7 +390,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
// TODO(benvanik): finish up BB
}
BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
BasicBlock* EmitterContext::GetBasicBlock(uint32_t address) {
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(address);
if (it != bbs_.end()) {
return it->second;
@ -379,7 +398,7 @@ BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
return NULL;
}
BasicBlock* FunctionGenerator::GetNextBasicBlock() {
BasicBlock* EmitterContext::GetNextBasicBlock() {
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(
fn_block_->start_address);
++it;
@ -389,21 +408,48 @@ BasicBlock* FunctionGenerator::GetNextBasicBlock() {
return NULL;
}
BasicBlock* FunctionGenerator::GetReturnBasicBlock() {
BasicBlock* EmitterContext::GetReturnBasicBlock() {
return return_block_;
}
Function* FunctionGenerator::GetFunction(FunctionSymbol* fn) {
Function* result = gen_module_->getFunction(StringRef(fn->name()));
if (!result) {
XELOGE("Static function not found: %.8X %s",
fn->start_address, fn->name());
Function* EmitterContext::GetFunction(FunctionSymbol* symbol) {
StringRef fn_name(symbol->name());
Function* fn = gen_module_->getFunction(fn_name);
if (fn) {
return fn;
}
XEASSERTNOTNULL(result);
return result;
fn = cast<Function>(gen_module_->getOrInsertFunction(fn_name, fn_type_));
fn->setVisibility(GlobalValue::DefaultVisibility);
// Indicate that the function will never be unwound with an exception.
// If we ever support native exception handling we may need to remove this.
fn->doesNotThrow();
// May be worth trying the X86_FastCall, as we only need state in a register.
//f->setCallingConv(CallingConv::Fast);
fn->setCallingConv(CallingConv::C);
Function::arg_iterator fn_args = fn->arg_begin();
// 'state'
Value* fn_arg = fn_args++;
fn_arg->setName("state");
fn->setDoesNotAlias(1);
fn->setDoesNotCapture(1);
// 'state' should try to be in a register, if possible.
// TODO(benvanik): verify that's a good idea.
// fn->getArgumentList().begin()->addAttr(
// Attribute::get(context, AttrBuilder().addAttribute(Attribute::InReg)));
// 'lr'
fn_arg = fn_args++;
fn_arg->setName("lr");
return fn;
}
int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
int EmitterContext::GenerateIndirectionBranch(uint32_t cia, Value* target,
bool lk, bool likely_local) {
// This function is called by the control emitters when they know that an
// indirect branch is required.
@ -489,7 +535,7 @@ int FunctionGenerator::GenerateIndirectionBranch(uint32_t cia, Value* target,
return 0;
}
Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
Value* EmitterContext::LoadStateValue(uint32_t offset, Type* type,
const char* name) {
IRBuilder<>& b = *builder_;
PointerType* pointerTy = PointerType::getUnqual(type);
@ -500,7 +546,7 @@ Value* FunctionGenerator::LoadStateValue(uint32_t offset, Type* type,
return b.CreateLoad(ptr, name);
}
void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
void EmitterContext::StoreStateValue(uint32_t offset, Type* type,
Value* value) {
IRBuilder<>& b = *builder_;
PointerType* pointerTy = PointerType::getUnqual(type);
@ -511,7 +557,7 @@ void FunctionGenerator::StoreStateValue(uint32_t offset, Type* type,
b.CreateStore(value, ptr);
}
void FunctionGenerator::SetupLocals() {
void EmitterContext::SetupLocals() {
IRBuilder<>& b = *builder_;
uint64_t spr_t = access_bits_.spr;
@ -559,7 +605,7 @@ void FunctionGenerator::SetupLocals() {
}
}
Value* FunctionGenerator::SetupLocal(llvm::Type* type, const char* name) {
Value* EmitterContext::SetupLocal(llvm::Type* type, const char* name) {
IRBuilder<>& b = *builder_;
// Insert into the entry block.
PushInsertPoint();
@ -569,11 +615,11 @@ Value* FunctionGenerator::SetupLocal(llvm::Type* type, const char* name) {
return v;
}
Value* FunctionGenerator::cia_value() {
Value* EmitterContext::cia_value() {
return builder_->getInt32(cia_);
}
void FunctionGenerator::FillRegisters() {
void EmitterContext::FillRegisters() {
// This updates all of the local register values from the state memory.
// It should be called on function entry for initial setup and after any
// calls that may modify the registers.
@ -637,7 +683,7 @@ void FunctionGenerator::FillRegisters() {
}
}
void FunctionGenerator::SpillRegisters() {
void EmitterContext::SpillRegisters() {
// This flushes all local registers (if written) to the register bank and
// resets their values.
//
@ -709,13 +755,13 @@ void FunctionGenerator::SpillRegisters() {
}
}
Value* FunctionGenerator::xer_value() {
Value* EmitterContext::xer_value() {
XEASSERTNOTNULL(locals_.xer);
IRBuilder<>& b = *builder_;
return b.CreateLoad(locals_.xer);
}
void FunctionGenerator::update_xer_value(Value* value) {
void EmitterContext::update_xer_value(Value* value) {
XEASSERTNOTNULL(locals_.xer);
IRBuilder<>& b = *builder_;
@ -726,7 +772,7 @@ void FunctionGenerator::update_xer_value(Value* value) {
b.CreateStore(value, locals_.xer);
}
void FunctionGenerator::update_xer_with_overflow(Value* value) {
void EmitterContext::update_xer_with_overflow(Value* value) {
XEASSERTNOTNULL(locals_.xer);
IRBuilder<>& b = *builder_;
@ -743,7 +789,7 @@ void FunctionGenerator::update_xer_with_overflow(Value* value) {
b.CreateStore(xer, locals_.xer);
}
void FunctionGenerator::update_xer_with_carry(Value* value) {
void EmitterContext::update_xer_with_carry(Value* value) {
XEASSERTNOTNULL(locals_.xer);
IRBuilder<>& b = *builder_;
@ -759,7 +805,7 @@ void FunctionGenerator::update_xer_with_carry(Value* value) {
b.CreateStore(xer, locals_.xer);
}
void FunctionGenerator::update_xer_with_overflow_and_carry(Value* value) {
void EmitterContext::update_xer_with_overflow_and_carry(Value* value) {
XEASSERTNOTNULL(locals_.xer);
IRBuilder<>& b = *builder_;
@ -779,13 +825,13 @@ void FunctionGenerator::update_xer_with_overflow_and_carry(Value* value) {
b.CreateStore(xer, locals_.xer);
}
Value* FunctionGenerator::lr_value() {
Value* EmitterContext::lr_value() {
XEASSERTNOTNULL(locals_.lr);
IRBuilder<>& b = *builder_;
return b.CreateLoad(locals_.lr);
}
void FunctionGenerator::update_lr_value(Value* value) {
void EmitterContext::update_lr_value(Value* value) {
XEASSERTNOTNULL(locals_.lr);
IRBuilder<>& b = *builder_;
@ -796,14 +842,14 @@ void FunctionGenerator::update_lr_value(Value* value) {
b.CreateStore(value, locals_.lr);
}
Value* FunctionGenerator::ctr_value() {
Value* EmitterContext::ctr_value() {
XEASSERTNOTNULL(locals_.ctr);
IRBuilder<>& b = *builder_;
return b.CreateLoad(locals_.ctr);
}
void FunctionGenerator::update_ctr_value(Value* value) {
void EmitterContext::update_ctr_value(Value* value) {
XEASSERTNOTNULL(locals_.ctr);
IRBuilder<>& b = *builder_;
@ -814,7 +860,7 @@ void FunctionGenerator::update_ctr_value(Value* value) {
b.CreateStore(value, locals_.ctr);
}
Value* FunctionGenerator::cr_value(uint32_t n) {
Value* EmitterContext::cr_value(uint32_t n) {
XEASSERT(n >= 0 && n < 8);
XEASSERTNOTNULL(locals_.cr[n]);
IRBuilder<>& b = *builder_;
@ -824,7 +870,7 @@ Value* FunctionGenerator::cr_value(uint32_t n) {
return v;
}
void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
void EmitterContext::update_cr_value(uint32_t n, Value* value) {
XEASSERT(n >= 0 && n < 8);
XEASSERTNOTNULL(locals_.cr[n]);
IRBuilder<>& b = *builder_;
@ -838,7 +884,7 @@ void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
b.CreateStore(value, locals_.cr[n]);
}
void FunctionGenerator::update_cr_with_cond(
void EmitterContext::update_cr_with_cond(
uint32_t n, Value* lhs, Value* rhs, bool is_signed) {
IRBuilder<>& b = *builder_;
@ -870,7 +916,7 @@ void FunctionGenerator::update_cr_with_cond(
update_cr_value(n, c);
}
Value* FunctionGenerator::gpr_value(uint32_t n) {
Value* EmitterContext::gpr_value(uint32_t n) {
XEASSERT(n >= 0 && n < 32);
XEASSERTNOTNULL(locals_.gpr[n]);
IRBuilder<>& b = *builder_;
@ -885,7 +931,7 @@ Value* FunctionGenerator::gpr_value(uint32_t n) {
return b.CreateLoad(locals_.gpr[n]);
}
void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
void EmitterContext::update_gpr_value(uint32_t n, Value* value) {
XEASSERT(n >= 0 && n < 32);
XEASSERTNOTNULL(locals_.gpr[n]);
IRBuilder<>& b = *builder_;
@ -904,14 +950,14 @@ void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
b.CreateStore(value, locals_.gpr[n]);
}
Value* FunctionGenerator::fpr_value(uint32_t n) {
Value* EmitterContext::fpr_value(uint32_t n) {
XEASSERT(n >= 0 && n < 32);
XEASSERTNOTNULL(locals_.fpr[n]);
IRBuilder<>& b = *builder_;
return b.CreateLoad(locals_.fpr[n]);
}
void FunctionGenerator::update_fpr_value(uint32_t n, Value* value) {
void EmitterContext::update_fpr_value(uint32_t n, Value* value) {
XEASSERT(n >= 0 && n < 32);
XEASSERTNOTNULL(locals_.fpr[n]);
IRBuilder<>& b = *builder_;
@ -919,12 +965,12 @@ void FunctionGenerator::update_fpr_value(uint32_t n, Value* value) {
b.CreateStore(value, locals_.fpr[n]);
}
Value* FunctionGenerator::GetMembase() {
Value* EmitterContext::GetMembase() {
Value* v = gen_module_->getGlobalVariable("xe_memory_base");
return builder_->CreateLoad(v);
}
Value* FunctionGenerator::GetMemoryAddress(uint32_t cia, Value* addr) {
Value* EmitterContext::GetMemoryAddress(uint32_t cia, Value* addr) {
IRBuilder<>& b = *builder_;
// Input address is always in 32-bit space.
@ -955,7 +1001,7 @@ Value* FunctionGenerator::GetMemoryAddress(uint32_t cia, Value* addr) {
return b.CreateInBoundsGEP(GetMembase(), addr);
}
Value* FunctionGenerator::ReadMemory(
Value* EmitterContext::ReadMemory(
uint32_t cia, Value* addr, uint32_t size, bool acquire) {
IRBuilder<>& b = *builder_;
@ -1004,7 +1050,7 @@ Value* FunctionGenerator::ReadMemory(
return value;
}
void FunctionGenerator::WriteMemory(
void EmitterContext::WriteMemory(
uint32_t cia, Value* addr, uint32_t size, Value* value, bool release) {
IRBuilder<>& b = *builder_;

View File

@ -7,8 +7,8 @@
******************************************************************************
*/
#ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#ifndef XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
#define XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
#include <llvm/IR/Attributes.h>
#include <llvm/IR/DataLayout.h>
@ -23,21 +23,22 @@
namespace xe {
namespace cpu {
namespace codegen {
namespace llvmbe {
class FunctionGenerator {
class EmitterContext {
public:
FunctionGenerator(
xe_memory_ref memory, sdb::SymbolDatabase* sdb, sdb::FunctionSymbol* fn,
llvm::LLVMContext* context, llvm::Module* gen_module,
llvm::Function* gen_fn);
~FunctionGenerator();
EmitterContext(
xe_memory_ref memory,
llvm::LLVMContext* context, llvm::Module* gen_module);
~EmitterContext();
int Init(sdb::FunctionSymbol* fn, llvm::Function* gen_fn);
sdb::SymbolDatabase* sdb();
sdb::FunctionSymbol* fn();
llvm::LLVMContext* context();
llvm::Module* gen_module();
sdb::FunctionSymbol* fn();
llvm::Function* gen_fn();
sdb::FunctionBlock* fn_block();
@ -49,7 +50,7 @@ public:
llvm::BasicBlock* GetNextBasicBlock();
llvm::BasicBlock* GetReturnBasicBlock();
llvm::Function* GetFunction(sdb::FunctionSymbol* fn);
llvm::Function* GetFunction(sdb::FunctionSymbol* symbol);
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
bool lk, bool likely_local);
@ -101,17 +102,18 @@ private:
void SetupLocals();
xe_memory_ref memory_;
sdb::SymbolDatabase* sdb_;
sdb::FunctionSymbol* fn_;
llvm::LLVMContext* context_;
llvm::Module* gen_module_;
llvm::IRBuilder<>* builder_;
llvm::FunctionType* fn_type_;
sdb::FunctionSymbol* fn_;
llvm::Function* gen_fn_;
sdb::FunctionBlock* fn_block_;
llvm::BasicBlock* return_block_;
llvm::BasicBlock* internal_indirection_block_;
llvm::BasicBlock* external_indirection_block_;
llvm::BasicBlock* bb_;
llvm::IRBuilder<>* builder_;
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
insert_points_;
@ -136,9 +138,9 @@ private:
};
} // namespace codegen
} // namespace llvmbe
} // namespace cpu
} // namespace xe
#endif // XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#endif // XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_

View File

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

View File

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

View File

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

View File

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

View File

@ -7,7 +7,7 @@
******************************************************************************
*/
#include <xenia/cpu/llvm_exports.h>
#include <xenia/cpu/llvmbe/llvm_exports.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/IR/Constants.h>
@ -16,95 +16,15 @@
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <xenia/cpu/sdb.h>
#include <xenia/cpu/ppc/instr.h>
#include <xenia/cpu/ppc/state.h>
#include <xenia/kernel/export.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
namespace {
void XeTrap(xe_ppc_state_t* state, uint32_t cia) {
XELOGE("TRAP");
XEASSERTALWAYS();
}
void XeIndirectBranch(xe_ppc_state_t* state, uint64_t target, uint64_t br_ia) {
XELOGCPU("INDIRECT BRANCH %.8X -> %.8X",
(uint32_t)br_ia, (uint32_t)target);
XEASSERTALWAYS();
}
void XeInvalidInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
ppc::InstrData i;
i.address = cia;
i.code = data;
i.type = ppc::GetInstrType(i.code);
if (!i.type) {
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X ???",
i.address, i.code);
} else if (i.type->disassemble) {
ppc::InstrDisasm d;
i.type->disassemble(i, d);
std::string disasm;
d.Dump(disasm);
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
i.address, i.code, disasm.c_str());
} else {
XELOGCPU("INVALID INSTRUCTION %.8X: %.8X %s",
i.address, i.code, i.type->name);
}
}
void XeAccessViolation(xe_ppc_state_t* state, uint32_t cia, uint64_t ea) {
XELOGE("INVALID ACCESS %.8X: tried to touch %.8X",
cia, (uint32_t)ea);
XEASSERTALWAYS();
}
void XeTraceKernelCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
KernelExport* kernel_export) {
XELOGCPU("TRACE: %.8X -> k.%.8X (%s)",
(uint32_t)call_ia - 4, (uint32_t)cia,
kernel_export ? kernel_export->name : "unknown");
}
void XeTraceUserCall(xe_ppc_state_t* state, uint64_t cia, uint64_t call_ia,
FunctionSymbol* fn) {
XELOGCPU("TRACE: %.8X -> u.%.8X (%s)",
(uint32_t)call_ia - 4, (uint32_t)cia, fn->name());
}
void XeTraceInstruction(xe_ppc_state_t* state, uint32_t cia, uint32_t data) {
ppc::InstrType* type = ppc::GetInstrType(data);
XELOGCPU("TRACE: %.8X %.8X %s %s",
cia, data,
type && type->emit ? " " : "X",
type ? type->name : "<unknown>");
if (cia == 0x82014468) {
printf("BREAKBREAKBREAK\n");
}
// TODO(benvanik): better disassembly, printing of current register values/etc
}
}
void xe::cpu::SetupLlvmExports(llvm::Module* module,
const llvm::DataLayout* dl,
llvm::ExecutionEngine* engine) {
void xe::cpu::llvmbe::SetupLlvmExports(
GlobalExports* global_exports,
Module* module, const DataLayout* dl, ExecutionEngine* engine) {
LLVMContext& context = module->getContext();
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
@ -116,7 +36,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
Type::getVoidTy(context), trapArgs, false);
engine->addGlobalMapping(Function::Create(
trapTy, Function::ExternalLinkage, "XeTrap",
module), (void*)&XeTrap);
module), (void*)(global_exports->XeTrap));
std::vector<Type*> indirectBranchArgs;
indirectBranchArgs.push_back(int8PtrTy);
@ -126,7 +46,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
Type::getVoidTy(context), indirectBranchArgs, false);
engine->addGlobalMapping(Function::Create(
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
module), (void*)&XeIndirectBranch);
module), (void*)(global_exports->XeIndirectBranch));
// Debugging methods:
std::vector<Type*> invalidInstructionArgs;
@ -137,7 +57,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
Type::getVoidTy(context), invalidInstructionArgs, false);
engine->addGlobalMapping(Function::Create(
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
module), (void*)&XeInvalidInstruction);
module), (void*)(global_exports->XeInvalidInstruction));
std::vector<Type*> accessViolationArgs;
accessViolationArgs.push_back(int8PtrTy);
@ -147,7 +67,7 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
Type::getVoidTy(context), accessViolationArgs, false);
engine->addGlobalMapping(Function::Create(
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
module), (void*)&XeAccessViolation);
module), (void*)(global_exports->XeAccessViolation));
// Tracing methods:
std::vector<Type*> traceCallArgs;
@ -166,11 +86,11 @@ void xe::cpu::SetupLlvmExports(llvm::Module* module,
engine->addGlobalMapping(Function::Create(
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
module), (void*)&XeTraceKernelCall);
module), (void*)(global_exports->XeTraceKernelCall));
engine->addGlobalMapping(Function::Create(
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
module), (void*)&XeTraceUserCall);
module), (void*)(global_exports->XeTraceUserCall));
engine->addGlobalMapping(Function::Create(
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
module), (void*)&XeTraceInstruction);
module), (void*)(global_exports->XeTraceInstruction));
}

View File

@ -7,16 +7,17 @@
******************************************************************************
*/
#ifndef XENIA_CPU_LLVM_EXPORTS_H_
#define XENIA_CPU_LLVM_EXPORTS_H_
#ifndef XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
#define XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
#include <xenia/common.h>
#include <xenia/core.h>
#include <xenia/cpu/global_exports.h>
namespace llvm {
class ExecutionEngine;
class LLVMContext;
class Module;
class DataLayout;
}
@ -24,15 +25,18 @@ namespace llvm {
namespace xe {
namespace cpu {
namespace llvmbe {
void SetupLlvmExports(llvm::Module* module,
const llvm::DataLayout* dl,
void SetupLlvmExports(
GlobalExports* global_exports,
llvm::Module* module, const llvm::DataLayout* dl,
llvm::ExecutionEngine* engine);
} // llvmbe
} // cpu
} // xe
#endif // XENIA_CPU_LLVM_EXPORTS_H_
#endif // XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_

View File

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

View File

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

View File

@ -7,14 +7,18 @@
******************************************************************************
*/
/**
* This file is shared between xethunk and the loader to pass structures
* between the two. Since this file is compiled with the LLVM clang it cannot
* include any other files.
*/
#ifndef XENIA_CPU_XETHUNK_H_
#define XENIA_CPU_XETHUNK_H_
#include <xenia/cpu/llvmbe/llvm_library_linker.h>
#endif // XENIA_CPU_XETHUNK_H_
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::llvmbe;
LLVMLibraryLinker::LLVMLibraryLinker(
xe_memory_ref memory, kernel::ExportResolver* export_resolver) :
LibraryLinker(memory, export_resolver) {
}
LLVMLibraryLinker::~LLVMLibraryLinker() {
}

View File

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

View File

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

View File

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

View File

@ -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',
],
}

View File

@ -717,22 +717,12 @@ XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
// TODO
return d.Finish();
}
XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
// No-op for now.
// TODO(benvanik): use @llvm.prefetch
return 0;
}
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
// TODO
return d.Finish();
}
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
// No-op for now.
// TODO(benvanik): use @llvm.prefetch
return 0;
}
XEDISASMR(dcbz, 0x7C0007EC, X )(InstrData& i, InstrDisasm& d) {
// or dcbz128 0x7C2007EC

View File

@ -9,19 +9,10 @@
#include <xenia/cpu/processor.h>
#include <llvm/ExecutionEngine/ExecutionEngine.h>
#include <llvm/ExecutionEngine/GenericValue.h>
#include <llvm/ExecutionEngine/Interpreter.h>
#include <llvm/ExecutionEngine/JIT.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <llvm/Support/ManagedStatic.h>
#include <llvm/Support/TargetSelect.h>
#include <xenia/cpu/codegen/emit.h>
#include <xenia/cpu/jit.h>
#include <xenia/cpu/ppc/disasm.h>
using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::kernel;
@ -38,36 +29,23 @@ namespace {
}
has_initialized = true;
// TODO(benvanik): only do this once
LLVMLinkInInterpreter();
LLVMLinkInJIT();
InitializeNativeTarget();
llvm_start_multithreaded();
ppc::RegisterDisasmCategoryALU();
ppc::RegisterDisasmCategoryControl();
ppc::RegisterDisasmCategoryFPU();
ppc::RegisterDisasmCategoryMemory();
// TODO(benvanik): only do this once
codegen::RegisterEmitCategoryALU();
codegen::RegisterEmitCategoryControl();
codegen::RegisterEmitCategoryFPU();
codegen::RegisterEmitCategoryMemory();
atexit(CleanupOnShutdown);
}
void CleanupOnShutdown() {
llvm_shutdown();
}
}
Processor::Processor(xe_pal_ref pal, xe_memory_ref memory) {
pal_ = xe_pal_retain(pal);
Processor::Processor(xe_memory_ref memory, shared_ptr<Backend> backend) :
fn_table_(NULL), jit_(NULL) {
memory_ = xe_memory_retain(memory);
backend_ = backend;
InitializeIfNeeded();
}
@ -76,55 +54,55 @@ Processor::~Processor() {
// Cleanup all modules.
for (std::vector<ExecModule*>::iterator it = modules_.begin();
it != modules_.end(); ++it) {
delete *it;
ExecModule* exec_module = *it;
if (jit_) {
jit_->UninitModule(exec_module);
}
delete exec_module;
}
modules_.clear();
engine_.reset();
delete jit_;
delete fn_table_;
export_resolver_.reset();
backend_.reset();
xe_memory_release(memory_);
xe_pal_release(pal_);
}
xe_pal_ref Processor::pal() {
return xe_pal_retain(pal_);
}
xe_memory_ref Processor::memory() {
return xe_memory_retain(memory_);
}
shared_ptr<ExportResolver> Processor::export_resolver() {
return export_resolver_;
}
void Processor::set_export_resolver(
shared_ptr<ExportResolver> export_resolver) {
export_resolver_ = export_resolver;
}
int Processor::Setup() {
XEASSERTNULL(engine_);
XEASSERTNULL(jit_);
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
Module* dummy_module = new Module("dummy", *dummy_context_.get());
fn_table_ = new FunctionTable();
std::string error_message;
EngineBuilder builder(dummy_module);
builder.setEngineKind(EngineKind::JIT);
builder.setErrorStr(&error_message);
builder.setOptLevel(CodeGenOpt::None);
//builder.setOptLevel(CodeGenOpt::Aggressive);
//builder.setTargetOptions();
builder.setAllocateGVsWithCode(false);
//builder.setUseMCJIT(true);
engine_ = shared_ptr<ExecutionEngine>(builder.create());
if (!engine_) {
jit_ = backend_->CreateJIT(memory_, fn_table_);
if (jit_->Setup()) {
XELOGE("Unable to create JIT");
return 1;
}
return 0;
}
int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<ExportResolver> export_resolver) {
int Processor::LoadRawBinary(const xechar_t* path, uint32_t start_address) {
ExecModule* exec_module = NULL;
const xechar_t* name = xestrrchr(path, '/') + 1;
// TODO(benvanik): map file from filesystem
xe_mmap_ref mmap = xe_mmap_open(pal_, kXEFileModeRead, path, 0, 0);
// TODO(benvanik): map file from filesystem API, not via platform API.
xe_mmap_ref mmap = xe_mmap_open(kXEFileModeRead, path, 0, 0);
if (!mmap) {
return NULL;
}
@ -133,30 +111,29 @@ int Processor::LoadBinary(const xechar_t* path, uint32_t start_address,
int result_code = 1;
// Place the data into memory at the desired address.
XEEXPECTZERO(xe_copy_memory(xe_memory_addr(memory_, start_address),
xe_memory_get_length(memory_),
addr, length));
// Prepare the module.
char name_a[XE_MAX_PATH];
XEEXPECTTRUE(xestrnarrow(name_a, XECOUNT(name_a), name));
char path_a[XE_MAX_PATH];
XEEXPECTTRUE(xestrnarrow(path_a, XECOUNT(path_a), path));
// Prepare the module.
// This will analyze it, generate code (if needed), and adds methods to
// the function table.
exec_module = new ExecModule(
memory_, export_resolver, name_a, path_a, engine_);
memory_, export_resolver_, fn_table_, name_a, path_a);
XEEXPECTZERO(exec_module->PrepareRawBinary(
start_address, start_address + (uint32_t)length));
if (exec_module->PrepareRawBinary(start_address,
start_address + (uint32_t)length)) {
delete exec_module;
return 1;
}
// Initialize the module and prepare it for execution.
XEEXPECTZERO(jit_->InitModule(exec_module));
exec_module->AddFunctionsToMap(all_fns_);
modules_.push_back(exec_module);
exec_module->Dump();
result_code = 0;
XECLEANUP:
if (result_code) {
@ -166,22 +143,28 @@ XECLEANUP:
return result_code;
}
int Processor::PrepareModule(const char* name, const char* path,
xe_xex2_ref xex,
shared_ptr<ExportResolver> export_resolver) {
int Processor::LoadXexModule(const char* name, const char* path,
xe_xex2_ref xex) {
int result_code = 1;
// Prepare the module.
// This will analyze it, generate code (if needed), and adds methods to
// the function table.
ExecModule* exec_module = new ExecModule(
memory_, export_resolver, name, path,
engine_);
memory_, export_resolver_, fn_table_, name, path);
XEEXPECTZERO(exec_module->PrepareXexModule(xex));
if (exec_module->PrepareXex(xex)) {
delete exec_module;
return 1;
}
// Initialize the module and prepare it for execution.
XEEXPECTZERO(jit_->InitModule(exec_module));
exec_module->AddFunctionsToMap(all_fns_);
modules_.push_back(exec_module);
return 0;
result_code = 0;
XECLEANUP:
if (result_code) {
delete exec_module;
}
return result_code;
}
uint32_t Processor::CreateCallback(void (*callback)(void* data), void* data) {
@ -200,14 +183,34 @@ void Processor::DeallocThread(ThreadState* thread_state) {
delete thread_state;
}
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
// Find the function to execute.
Function* f = GetFunction(address);
if (!f) {
XELOGCPU("Failed to find function %.8X to execute.", address);
return 1;
FunctionPointer Processor::GenerateFunction(uint32_t address) {
// Search all modules for the function symbol.
// Each module will see if the address is within its code range and if the
// symbol is not found (likely) it will do analysis on it.
// TODO(benvanik): make this more efficient. Could use a binary search or
// something more clever.
sdb::FunctionSymbol* fn_symbol = NULL;
for (std::vector<ExecModule*>::iterator it = modules_.begin();
it != modules_.end(); ++it) {
fn_symbol = (*it)->FindFunctionSymbol(address);
if (fn_symbol) {
break;
}
}
if (!fn_symbol) {
return NULL;
}
// JIT the function.
FunctionPointer f = jit_->GenerateFunction(fn_symbol);
if (!f) {
return NULL;
}
return f;
}
int Processor::Execute(ThreadState* thread_state, uint32_t address) {
xe_ppc_state_t* ppc_state = thread_state->ppc_state();
// This could be set to anything to give us a unique identifier to track
@ -217,22 +220,23 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
// Setup registers.
ppc_state->lr = lr;
// Args:
// - i8* state
// - i64 lr
std::vector<GenericValue> args;
args.push_back(PTOGV(ppc_state));
GenericValue lr_arg;
lr_arg.IntVal = APInt(64, lr);
args.push_back(lr_arg);
GenericValue ret = engine_->runFunction(f, args);
// return (uint32_t)ret.IntVal.getSExtValue();
// Find the function to execute.
FunctionPointer f = fn_table_->GetFunction(address);
// Faster, somewhat.
// JIT, if needed.
if (!f) {
f = this->GenerateFunction(address);
}
// If JIT failed, die.
if (!f) {
XELOGCPU("Execute(%.8X): failed to find function", address);
return 1;
}
// Execute the function pointer.
// Messes with the stack in such a way as to cause Xcode to behave oddly.
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
// ptr(ppc_state, lr);
f(ppc_state, lr);
return 0;
}
@ -246,11 +250,3 @@ uint64_t Processor::Execute(ThreadState* thread_state, uint32_t address,
}
return ppc_state->r[3];
}
Function* Processor::GetFunction(uint32_t address) {
FunctionMap::iterator it = all_fns_.find(address);
if (it != all_fns_.end()) {
return it->second;
}
return NULL;
}

View File

@ -14,36 +14,33 @@
#include <vector>
#include <xenia/cpu/backend.h>
#include <xenia/cpu/exec_module.h>
#include <xenia/cpu/function_table.h>
#include <xenia/cpu/thread_state.h>
#include <xenia/kernel/export.h>
#include <xenia/kernel/xex2.h>
namespace llvm {
class ExecutionEngine;
class Function;
}
namespace xe {
namespace cpu {
class JIT;
class Processor {
public:
Processor(xe_memory_ref memory);
Processor(xe_memory_ref memory, shared_ptr<Backend> backend);
~Processor();
xe_memory_ref memory();
shared_ptr<kernel::ExportResolver> export_resolver();
void set_export_resolver(shared_ptr<kernel::ExportResolver> export_resolver);
int Setup();
int LoadBinary(const xechar_t* path, uint32_t start_address,
shared_ptr<kernel::ExportResolver> export_resolver);
int PrepareModule(const char* name, const char* path, xe_xex2_ref xex,
shared_ptr<kernel::ExportResolver> export_resolver);
int LoadRawBinary(const xechar_t* path, uint32_t start_address);
int LoadXexModule(const char* name, const char* path, xe_xex2_ref xex);
uint32_t CreateCallback(void (*callback)(void* data), void* data);
@ -53,16 +50,15 @@ public:
uint64_t Execute(ThreadState* thread_state, uint32_t address, uint64_t arg0);
private:
llvm::Function* GetFunction(uint32_t address);
FunctionPointer GenerateFunction(uint32_t address);
xe_memory_ref memory_;
shared_ptr<llvm::ExecutionEngine> engine_;
auto_ptr<llvm::LLVMContext> dummy_context_;
shared_ptr<Backend> backend_;
shared_ptr<kernel::ExportResolver> export_resolver_;
FunctionTable* fn_table_;
JIT* jit_;
std::vector<ExecModule*> modules_;
FunctionMap all_fns_;
};

View File

@ -105,6 +105,11 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
return NULL;
}
void FunctionSymbol::AddCall(FunctionSymbol* source, FunctionSymbol* target) {
source->outgoing_calls.push_back(FunctionCall(0, source, target));
target->incoming_calls.push_back(FunctionCall(0, source, target));
}
VariableSymbol::VariableSymbol() :
Symbol(Variable),

View File

@ -32,6 +32,10 @@ public:
uint32_t address;
FunctionSymbol* source;
FunctionSymbol* target;
FunctionCall(uint32_t address, FunctionSymbol* source,
FunctionSymbol* target) :
address(address), source(source), target(target) {}
};
class VariableAccess {
@ -39,6 +43,10 @@ public:
uint32_t address;
FunctionSymbol* source;
VariableSymbol* target;
VariableAccess(uint32_t address, FunctionSymbol* source,
VariableSymbol* target) :
address(address), source(source), target(target) {}
};
class Symbol {
@ -116,11 +124,13 @@ public:
kernel::KernelExport* kernel_export;
ExceptionEntrySymbol* ee;
std::vector<FunctionCall*> incoming_calls;
std::vector<FunctionCall*> outgoing_calls;
std::vector<VariableAccess*> variable_accesses;
std::vector<FunctionCall> incoming_calls;
std::vector<FunctionCall> outgoing_calls;
std::vector<VariableAccess> variable_accesses;
std::map<uint32_t, FunctionBlock*> blocks;
static void AddCall(FunctionSymbol* source, FunctionSymbol* target);
};
class VariableSymbol : public Symbol {

View File

@ -103,9 +103,13 @@ ExceptionEntrySymbol* SymbolDatabase::GetOrInsertExceptionEntry(
return ee;
}
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
FunctionSymbol* SymbolDatabase::GetOrInsertFunction(
uint32_t address, FunctionSymbol* opt_call_source) {
FunctionSymbol* fn = GetFunction(address);
if (fn) {
if (opt_call_source) {
FunctionSymbol::AddCall(opt_call_source, fn);
}
return fn;
}
@ -120,6 +124,11 @@ FunctionSymbol* SymbolDatabase::GetOrInsertFunction(uint32_t address) {
function_count_++;
symbols_.insert(SymbolMap::value_type(address, fn));
scan_queue_.push_back(fn);
if (opt_call_source) {
FunctionSymbol::AddCall(opt_call_source, fn);
}
return fn;
}
@ -460,7 +469,9 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
// Function call.
block->outgoing_type = FunctionBlock::kTargetFunction;
block->outgoing_function = GetFunction(block->outgoing_address);
if (!block->outgoing_function) {
if (block->outgoing_function) {
FunctionSymbol::AddCall(fn, block->outgoing_function);
} else {
XELOGE("call target not found: %.8X -> %.8X",
block->end_address, block->outgoing_address);
new_fns.push_back(block->outgoing_address);
@ -474,7 +485,7 @@ int SymbolDatabase::CompleteFunctionGraph(FunctionSymbol* fn) {
(uint32_t)new_fns.size());
for (std::vector<uint32_t>::iterator it = new_fns.begin();
it != new_fns.end(); ++it) {
GetOrInsertFunction(*it);
GetOrInsertFunction(*it, fn);
}
return 1;
}

View File

@ -34,7 +34,8 @@ public:
Symbol* GetSymbol(uint32_t address);
ExceptionEntrySymbol* GetOrInsertExceptionEntry(uint32_t address);
FunctionSymbol* GetOrInsertFunction(uint32_t address);
FunctionSymbol* GetOrInsertFunction(
uint32_t address, FunctionSymbol* opt_call_source = NULL);
VariableSymbol* GetOrInsertVariable(uint32_t address);
FunctionSymbol* GetFunction(uint32_t address);
VariableSymbol* GetVariable(uint32_t address);

View File

@ -1,13 +1,20 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'backend.h',
'code_unit_builder.h',
'cpu-private.h',
'cpu.cc',
'cpu.h',
'exec_module.cc',
'exec_module.h',
'llvm_exports.cc',
'llvm_exports.h',
'function_table.cc',
'function_table.h',
'global_exports.cc',
'global_exports.h',
'jit.h',
'library_linker.h',
'library_loader.h',
'ppc.h',
'processor.cc',
'processor.h',
@ -16,7 +23,7 @@
],
'includes': [
'codegen/sources.gypi',
'llvmbe/sources.gypi',
'ppc/sources.gypi',
'sdb/sources.gypi',
],

Binary file not shown.

View File

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

View File

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

View File

@ -28,6 +28,7 @@ public:
private:
xe_memory_ref memory_;
shared_ptr<Backend> backend_;
shared_ptr<Processor> processor_;
shared_ptr<Runtime> runtime_;
shared_ptr<Debugger> debugger_;
@ -45,6 +46,8 @@ int Run::Setup() {
xe_zero_struct(&pal_options, sizeof(pal_options));
XEEXPECTZERO(xe_pal_init(pal_options));
backend_ = shared_ptr<Backend>(new xe::cpu::llvmbe::LLVMBackend());
debugger_ = shared_ptr<Debugger>(new Debugger());
xe_memory_options_t memory_options;
@ -52,10 +55,11 @@ int Run::Setup() {
memory_ = xe_memory_create(memory_options);
XEEXPECTNOTNULL(memory_);
processor_ = shared_ptr<Processor>(new Processor(memory_));
processor_ = shared_ptr<Processor>(new Processor(memory_, backend_));
XEEXPECTZERO(processor_->Setup());
runtime_ = shared_ptr<Runtime>(new Runtime(processor_, XT("")));
processor_->set_export_resolver(runtime_->export_resolver());
return 0;
XECLEANUP: