Work on CPU, codegen skeleton, and thunk.
This commit is contained in:
parent
2f4bc598e5
commit
099e37490a
|
@ -13,6 +13,7 @@
|
|||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/kernel/export.h>
|
||||
#include <xenia/kernel/module.h>
|
||||
|
||||
|
||||
|
@ -33,7 +34,8 @@ void xe_cpu_release(xe_cpu_ref cpu);
|
|||
xe_pal_ref xe_cpu_get_pal(xe_cpu_ref cpu);
|
||||
xe_memory_ref xe_cpu_get_memory(xe_cpu_ref cpu);
|
||||
|
||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module);
|
||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module,
|
||||
xe_kernel_export_resolver_ref export_resolver);
|
||||
|
||||
int xe_cpu_execute(xe_cpu_ref cpu, uint32_t address);
|
||||
|
||||
|
|
|
@ -0,0 +1,194 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 "cpu/codegen.h"
|
||||
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
||||
void xe_cpu_codegen_add_imports(xe_memory_ref memory,
|
||||
xe_kernel_export_resolver_ref export_resolver,
|
||||
xe_module_ref module, Module *m);
|
||||
void xe_cpu_codegen_add_missing_import(
|
||||
Module *m, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export);
|
||||
void xe_cpu_codegen_add_import(
|
||||
Module *m, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export);
|
||||
void xe_cpu_codegen_optimize(Module *m, Function *fn);
|
||||
|
||||
|
||||
llvm::Module *xe_cpu_codegen(llvm::LLVMContext& context, xe_memory_ref memory,
|
||||
xe_kernel_export_resolver_ref export_resolver,
|
||||
xe_module_ref module, Module *shared_module,
|
||||
xe_codegen_options_t options) {
|
||||
std::string error_message;
|
||||
|
||||
// Initialize the module.
|
||||
Module *m = new Module("generated.xex", context);
|
||||
// TODO(benavnik): addModuleFlag?
|
||||
|
||||
// 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(m, shared_module, 0, &error_message);
|
||||
|
||||
// Add import thunks/etc.
|
||||
xe_cpu_codegen_add_imports(memory, export_resolver, module, m);
|
||||
|
||||
// Add export wrappers.
|
||||
//
|
||||
|
||||
Constant* c = m->getOrInsertFunction("mul_add",
|
||||
/*ret type*/ IntegerType::get(context, 32),
|
||||
/*args*/ IntegerType::get(context, 32),
|
||||
IntegerType::get(context, 32),
|
||||
IntegerType::get(context, 32),
|
||||
/*varargs terminated with null*/ NULL);
|
||||
|
||||
Function* mul_add = cast<Function>(c);
|
||||
mul_add->setCallingConv(CallingConv::C);
|
||||
|
||||
Function::arg_iterator args = mul_add->arg_begin();
|
||||
Value* x = args++;
|
||||
x->setName("x");
|
||||
Value* y = args++;
|
||||
y->setName("y");
|
||||
Value* z = args++;
|
||||
z->setName("z");
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(getGlobalContext(), "entry", mul_add);
|
||||
IRBuilder<> builder(block);
|
||||
|
||||
Value* tmp = builder.CreateBinOp(Instruction::Mul,
|
||||
x, y, "tmp");
|
||||
Value* tmp2 = builder.CreateBinOp(Instruction::Add,
|
||||
tmp, z, "tmp2");
|
||||
|
||||
builder.CreateRet(tmp2);
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
xe_cpu_codegen_optimize(m, mul_add);
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
void xe_cpu_codegen_add_imports(xe_memory_ref memory,
|
||||
xe_kernel_export_resolver_ref export_resolver,
|
||||
xe_module_ref module, Module *m) {
|
||||
xe_xex2_ref xex = xe_module_get_xex(module);
|
||||
const xe_xex2_header_t *header = xe_xex2_get_header(xex);
|
||||
|
||||
for (size_t n = 0; n < header->import_library_count; n++) {
|
||||
const xe_xex2_import_library_t *library = &header->import_libraries[n];
|
||||
|
||||
xe_xex2_import_info_t* import_infos;
|
||||
size_t import_info_count;
|
||||
XEIGNORE(xe_xex2_get_import_infos(xex, library,
|
||||
&import_infos, &import_info_count));
|
||||
|
||||
for (size_t i = 0; i < import_info_count; i++) {
|
||||
const xe_xex2_import_info_t *info = &import_infos[i];
|
||||
xe_kernel_export_t *kernel_export =
|
||||
xe_kernel_export_resolver_get_by_ordinal(
|
||||
export_resolver, library->name, info->ordinal);
|
||||
if (!kernel_export || !xe_kernel_export_is_implemented(kernel_export)) {
|
||||
// Not implemented or known.
|
||||
xe_cpu_codegen_add_missing_import(m, library, info, kernel_export);
|
||||
} else {
|
||||
// Implemented.
|
||||
xe_cpu_codegen_add_import(m, library, info, kernel_export);
|
||||
}
|
||||
}
|
||||
|
||||
xe_free(import_infos);
|
||||
}
|
||||
|
||||
xe_xex2_release(xex);
|
||||
}
|
||||
|
||||
void xe_cpu_codegen_add_missing_import(
|
||||
Module *m, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export) {
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
char name[128];
|
||||
xesnprintfa(name, XECOUNT(name), "__%s_%.8X",
|
||||
library->name, kernel_export->ordinal);
|
||||
|
||||
// TODO(benvanik): add name as comment/alias?
|
||||
// name = kernel_export->name;
|
||||
|
||||
if (info->thunk_address) {
|
||||
AttributeWithIndex awi[] = {
|
||||
//AttributeWithIndex::get(context, 2, Attributes::NoCapture),
|
||||
AttributeWithIndex::get(context,
|
||||
AttributeSet::FunctionIndex, Attribute::NoUnwind),
|
||||
};
|
||||
AttributeSet attrs = AttributeSet::get(context, awi);
|
||||
|
||||
std::vector<Type*> args;
|
||||
Type *return_type = Type::getInt32Ty(context);
|
||||
|
||||
FunctionType *ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function *f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(name), ft, attrs));
|
||||
f->setCallingConv(CallingConv::C);
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> builder(block);
|
||||
Value *tmp = builder.getInt32(0);
|
||||
builder.getInt32(123);
|
||||
builder.CreateRet(tmp);
|
||||
|
||||
xe_cpu_codegen_optimize(m, f);
|
||||
|
||||
//GlobalAlias *alias = new GlobalAlias(f->getType(), GlobalValue::InternalLinkage, name, f, m);
|
||||
// printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->thunk_address, info->ordinal,
|
||||
// info->ordinal, implemented ? " " : "!!", name);
|
||||
} else {
|
||||
// printf(" V %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->ordinal, info->ordinal,
|
||||
// implemented ? " " : "!!", name);
|
||||
}
|
||||
}
|
||||
|
||||
void xe_cpu_codegen_add_import(
|
||||
Module *m, const xe_xex2_import_library_t *library,
|
||||
const xe_xex2_import_info_t* info, xe_kernel_export_t *kernel_export) {
|
||||
//
|
||||
}
|
||||
|
||||
void xe_cpu_codegen_optimize(Module *m, Function *fn) {
|
||||
FunctionPassManager pm(m);
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(pm);
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*fn);
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_H_
|
||||
#define XENIA_CPU_CODEGEN_H_
|
||||
|
||||
#include <xenia/core/memory.h>
|
||||
#include <xenia/kernel/module.h>
|
||||
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
|
||||
typedef struct {
|
||||
int reserved;
|
||||
} xe_codegen_options_t;
|
||||
|
||||
|
||||
llvm::Module *xe_cpu_codegen(llvm::LLVMContext& context, xe_memory_ref memory,
|
||||
xe_kernel_export_resolver_ref export_resolver,
|
||||
xe_module_ref module, llvm::Module *shared_module,
|
||||
xe_codegen_options_t options);
|
||||
|
||||
|
||||
#endif // XENIA_CPU_CODEGEN_H_
|
255
src/cpu/cpu.cc
255
src/cpu/cpu.cc
|
@ -9,14 +9,39 @@
|
|||
|
||||
#include <xenia/cpu.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/ExecutionEngine/Interpreter.h>
|
||||
#include <llvm/ExecutionEngine/JIT.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/Support/Host.h>
|
||||
#include <llvm/Support/ManagedStatic.h>
|
||||
#include <llvm/Support/MemoryBuffer.h>
|
||||
#include <llvm/Support/raw_ostream.h>
|
||||
#include <llvm/Support/system_error.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include "cpu/codegen.h"
|
||||
#include "cpu/xethunk/xethunk.h"
|
||||
|
||||
using namespace llvm;
|
||||
|
||||
|
||||
typedef struct {
|
||||
xe_module_ref module;
|
||||
LLVMContext *context;
|
||||
Module *m;
|
||||
} xe_cpu_module_entry_t;
|
||||
|
||||
typedef struct xe_cpu {
|
||||
xe_ref_t ref;
|
||||
|
||||
|
@ -24,9 +49,18 @@ typedef struct xe_cpu {
|
|||
|
||||
xe_pal_ref pal;
|
||||
xe_memory_ref memory;
|
||||
|
||||
std::vector<xe_cpu_module_entry_t> entries;
|
||||
|
||||
ExecutionEngine *engine;
|
||||
} xe_cpu_t;
|
||||
|
||||
|
||||
int xe_cpu_setup_engine(xe_cpu_ref cpu, Module *gen_module);
|
||||
int xe_cpu_init_module(xe_cpu_ref, Module *gen_module);
|
||||
void xe_cpu_uninit_module(xe_cpu_ref cpu, xe_cpu_module_entry_t *module_entry);
|
||||
|
||||
|
||||
xe_cpu_ref xe_cpu_create(xe_pal_ref pal, xe_memory_ref memory,
|
||||
xe_cpu_options_t options) {
|
||||
xe_cpu_ref cpu = (xe_cpu_ref)xe_calloc(sizeof(xe_cpu));
|
||||
|
@ -37,10 +71,32 @@ xe_cpu_ref xe_cpu_create(xe_pal_ref pal, xe_memory_ref memory,
|
|||
cpu->pal = xe_pal_retain(pal);
|
||||
cpu->memory = xe_memory_retain(memory);
|
||||
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
XEEXPECTTRUE(llvm_start_multithreaded());
|
||||
|
||||
return cpu;
|
||||
|
||||
XECLEANUP:
|
||||
xe_cpu_release(cpu);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void xe_cpu_dealloc(xe_cpu_ref cpu) {
|
||||
// Cleanup all modules.
|
||||
for (std::vector<xe_cpu_module_entry_t>::iterator it = cpu->entries.begin();
|
||||
it != cpu->entries.end(); ++it) {
|
||||
xe_cpu_uninit_module(cpu, &*it);
|
||||
cpu->engine->removeModule(it->m);
|
||||
delete it->m;
|
||||
delete it->context;
|
||||
xe_module_release(it->module);
|
||||
}
|
||||
|
||||
delete cpu->engine;
|
||||
llvm_shutdown();
|
||||
|
||||
xe_memory_release(cpu->memory);
|
||||
xe_pal_release(cpu->pal);
|
||||
}
|
||||
|
@ -62,50 +118,161 @@ xe_memory_ref xe_cpu_get_memory(xe_cpu_ref cpu) {
|
|||
return xe_memory_retain(cpu->memory);
|
||||
}
|
||||
|
||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module) {
|
||||
// TODO(benvanik): lookup the module in the cache.
|
||||
|
||||
// TODO(benvanik): implement prepare module.
|
||||
|
||||
XELOGCPU(XT("cpu"));
|
||||
|
||||
LLVMContext &context = getGlobalContext();
|
||||
//IRBuilder<> builder(context);
|
||||
Module *m = new Module("my cool jit", context);
|
||||
|
||||
Constant* c = m->getOrInsertFunction("mul_add",
|
||||
/*ret type*/ IntegerType::get(context, 32),
|
||||
/*args*/ IntegerType::get(context, 32),
|
||||
IntegerType::get(context, 32),
|
||||
IntegerType::get(context, 32),
|
||||
/*varargs terminated with null*/ NULL);
|
||||
|
||||
Function* mul_add = cast<Function>(c);
|
||||
mul_add->setCallingConv(CallingConv::C);
|
||||
|
||||
Function::arg_iterator args = mul_add->arg_begin();
|
||||
Value* x = args++;
|
||||
x->setName("x");
|
||||
Value* y = args++;
|
||||
y->setName("y");
|
||||
Value* z = args++;
|
||||
z->setName("z");
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(getGlobalContext(), "entry", mul_add);
|
||||
IRBuilder<> builder(block);
|
||||
|
||||
Value* tmp = builder.CreateBinOp(Instruction::Mul,
|
||||
x, y, "tmp");
|
||||
Value* tmp2 = builder.CreateBinOp(Instruction::Add,
|
||||
tmp, z, "tmp2");
|
||||
|
||||
builder.CreateRet(tmp2);
|
||||
|
||||
XELOGD(XT("woo %d"), 123);
|
||||
|
||||
m->dump();
|
||||
delete m;
|
||||
int xe_cpu_setup_engine(xe_cpu_ref cpu, Module *gen_module) {
|
||||
if (cpu->engine) {
|
||||
// Engine already initialized - just add the module.
|
||||
cpu->engine->addModule(gen_module);
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::string error_message;
|
||||
cpu->engine = ExecutionEngine::create(gen_module, false, &error_message,
|
||||
CodeGenOpt::Aggressive);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module,
|
||||
xe_kernel_export_resolver_ref export_resolver) {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
LLVMContext *context = NULL;
|
||||
OwningPtr<MemoryBuffer> shared_module_buffer;
|
||||
Module *gen_module = NULL;
|
||||
Module *shared_module = NULL;
|
||||
raw_ostream *outs = NULL;
|
||||
|
||||
PassManager pm;
|
||||
PassManagerBuilder pmb;
|
||||
|
||||
// TODO(benvanik): embed the bc file into the emulator.
|
||||
const char *thunk_path = "src/cpu/xethunk/xethunk.bc";
|
||||
|
||||
// Create a LLVM context for this prepare.
|
||||
// This is required to ensure thread safety/etc.
|
||||
context = new LLVMContext();
|
||||
|
||||
// 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)) {
|
||||
// gen_module = load_bitcode(cache_key);
|
||||
// }
|
||||
|
||||
// If not found in cache, generate a new module.
|
||||
if (!gen_module) {
|
||||
// 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 = ParseBitcodeFile(&*shared_module_buffer, *context,
|
||||
&error_message);
|
||||
XEEXPECTNOTNULL(shared_module);
|
||||
|
||||
// Build the module from the source code.
|
||||
xe_codegen_options_t codegen_options;
|
||||
xe_zero_struct(&codegen_options, sizeof(codegen_options));
|
||||
gen_module = xe_cpu_codegen(*context, cpu->memory, export_resolver,
|
||||
module, shared_module,
|
||||
codegen_options);
|
||||
|
||||
// Write to cache.
|
||||
outs = new raw_fd_ostream(cache_path, error_message,
|
||||
raw_fd_ostream::F_Binary);
|
||||
XEEXPECTTRUE(error_message.empty());
|
||||
WriteBitcodeToFile(gen_module, *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));
|
||||
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);
|
||||
|
||||
// TODO(benvanik): experiment with LLD to see if we can write out a dll.
|
||||
|
||||
// Setup the execution engine (if needed).
|
||||
// The engine is required to get the data layout and other values.
|
||||
XEEXPECTZERO(xe_cpu_setup_engine(cpu, gen_module));
|
||||
|
||||
// Initialize the module.
|
||||
XEEXPECTZERO(xe_cpu_init_module(cpu, gen_module));
|
||||
|
||||
// Force JIT of all functions.
|
||||
for (Module::iterator I = gen_module->begin(), E = gen_module->end();
|
||||
I != E; I++) {
|
||||
Function* fn = &*I;
|
||||
if (!fn->isDeclaration()) {
|
||||
cpu->engine->getPointerToFunction(fn);
|
||||
}
|
||||
}
|
||||
|
||||
gen_module->dump();
|
||||
|
||||
// Stash the module entry to allow cleanup later.
|
||||
xe_cpu_module_entry_t module_entry;
|
||||
module_entry.module = xe_module_retain(module);
|
||||
module_entry.context = context;
|
||||
module_entry.m = gen_module;
|
||||
cpu->entries.push_back(module_entry);
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
delete outs;
|
||||
delete shared_module;
|
||||
if (result_code) {
|
||||
delete gen_module;
|
||||
delete context;
|
||||
}
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int xe_cpu_init_module(xe_cpu_ref cpu, Module *gen_module) {
|
||||
// Run static initializers. I'm not sure we'll have any, but who knows.
|
||||
cpu->engine->runStaticConstructorsDestructors(gen_module, false);
|
||||
|
||||
// Prepare init options.
|
||||
xe_module_init_options_t init_options;
|
||||
xe_zero_struct(&init_options, sizeof(init_options));
|
||||
init_options.memory_base = xe_memory_addr(cpu->memory, 0);
|
||||
|
||||
// Grab the init function and call it.
|
||||
Function *xe_module_init = gen_module->getFunction("xe_module_init");
|
||||
std::vector<GenericValue> args;
|
||||
args.push_back(GenericValue(&init_options));
|
||||
GenericValue ret = cpu->engine->runFunction(xe_module_init, args);
|
||||
|
||||
return ret.IntVal.getSExtValue();
|
||||
}
|
||||
|
||||
void xe_cpu_uninit_module(xe_cpu_ref cpu, xe_cpu_module_entry_t *module_entry) {
|
||||
// Grab function and call it.
|
||||
Function *xe_module_uninit = module_entry->m->getFunction("xe_module_uninit");
|
||||
std::vector<GenericValue> args;
|
||||
cpu->engine->runFunction(xe_module_uninit, args);
|
||||
|
||||
// Run static destructors.
|
||||
cpu->engine->runStaticConstructorsDestructors(module_entry->m, true);
|
||||
}
|
||||
|
||||
int xe_cpu_execute(xe_cpu_ref cpu, uint32_t address) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'codegen.cc',
|
||||
'cpu.cc',
|
||||
],
|
||||
}
|
||||
|
|
Binary file not shown.
|
@ -0,0 +1,39 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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"
|
||||
|
||||
|
||||
// The base pointer that all guest addresses should be relative to.
|
||||
static void* xe_memory_base;
|
||||
|
||||
|
||||
int xe_module_init(xe_module_init_options_t *options) {
|
||||
xe_memory_base = options->memory_base;
|
||||
|
||||
// TODO(benvanik): setup call table, etc?
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void xe_module_uninit() {
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 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_XELIB_H_
|
||||
#define XENIA_CPU_XELIB_H_
|
||||
|
||||
|
||||
typedef struct {
|
||||
void* memory_base;
|
||||
|
||||
// TODO(benvanik): execute call thunk
|
||||
} xe_module_init_options_t;
|
||||
|
||||
|
||||
#endif // XENIA_CPU_XELIB_H_
|
|
@ -0,0 +1,21 @@
|
|||
; 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"
|
||||
|
||||
%struct.xe_module_init_options_t = type { i8* }
|
||||
|
||||
@xe_memory_base = internal global i8* null, align 8
|
||||
|
||||
define i32 @xe_module_init(%struct.xe_module_init_options_t* %options) nounwind uwtable ssp {
|
||||
%1 = alloca %struct.xe_module_init_options_t*, align 8
|
||||
store %struct.xe_module_init_options_t* %options, %struct.xe_module_init_options_t** %1, align 8
|
||||
%2 = load %struct.xe_module_init_options_t** %1, align 8
|
||||
%3 = getelementptr inbounds %struct.xe_module_init_options_t* %2, i32 0, i32 0
|
||||
%4 = load i8** %3, align 8
|
||||
store i8* %4, i8** @xe_memory_base, align 8
|
||||
ret i32 0
|
||||
}
|
||||
|
||||
define void @xe_module_uninit() nounwind uwtable ssp {
|
||||
ret void
|
||||
}
|
|
@ -122,7 +122,8 @@ xe_module_ref xe_kernel_load_module(xe_kernel_ref kernel,
|
|||
}
|
||||
|
||||
// Prepare the module.
|
||||
XEEXPECTZERO(xe_cpu_prepare_module(kernel->cpu, module));
|
||||
XEEXPECTZERO(xe_cpu_prepare_module(kernel->cpu, module,
|
||||
kernel->export_resolver));
|
||||
|
||||
// Stash in modules list (takes reference).
|
||||
// TODO(benvanik): stash in list.
|
||||
|
|
|
@ -61,6 +61,7 @@ def discover_commands():
|
|||
'pull': PullCommand(),
|
||||
'gyp': GypCommand(),
|
||||
'build': BuildCommand(),
|
||||
'xethunk': XethunkCommand(),
|
||||
'clean': CleanCommand(),
|
||||
'nuke': NukeCommand(),
|
||||
}
|
||||
|
@ -379,6 +380,33 @@ class BuildCommand(Command):
|
|||
return 0
|
||||
|
||||
|
||||
class XethunkCommand(Command):
|
||||
"""'xethunk' command."""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(XethunkCommand, self).__init__(
|
||||
name='xethunk',
|
||||
help_short='Updates the xethunk.bc file.',
|
||||
*args, **kwargs)
|
||||
|
||||
def execute(self, args, cwd):
|
||||
print 'Building xethunk...'
|
||||
print ''
|
||||
|
||||
path = 'src/cpu/xethunk/xethunk'
|
||||
result = shell_call('clang -emit-llvm -O0 -c %s.c -o %s.bc' % (path, path),
|
||||
throw_on_error=False)
|
||||
if result != 0:
|
||||
return result
|
||||
|
||||
shell_call('build/llvm/release/bin/llvm-dis %s.bc -o %s.ll' % (path, path))
|
||||
|
||||
shell_call('cat %s.ll' % (path))
|
||||
|
||||
print 'Success!'
|
||||
return 0
|
||||
|
||||
|
||||
class CleanCommand(Command):
|
||||
"""'clean' command."""
|
||||
|
||||
|
|
|
@ -74,7 +74,7 @@
|
|||
['_type=="executable"', {
|
||||
'libraries': [
|
||||
'<!@(<(llvm_config) --ldflags)',
|
||||
'<!@(<(llvm_config) --libs core)',
|
||||
'<!@(<(llvm_config) --libs all)',
|
||||
],
|
||||
'library_dirs': [
|
||||
# NOTE: this doesn't actually do anything...
|
||||
|
@ -84,7 +84,7 @@
|
|||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
'<!@(<(llvm_config) --ldflags)',
|
||||
'<!@(<(llvm_config) --libs core)',
|
||||
'<!@(<(llvm_config) --libs all)',
|
||||
],
|
||||
},
|
||||
}],
|
||||
|
|
Loading…
Reference in New Issue