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/common.h>
|
||||||
#include <xenia/core.h>
|
#include <xenia/core.h>
|
||||||
|
|
||||||
|
#include <xenia/kernel/export.h>
|
||||||
#include <xenia/kernel/module.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_pal_ref xe_cpu_get_pal(xe_cpu_ref cpu);
|
||||||
xe_memory_ref xe_cpu_get_memory(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);
|
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 <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/DerivedTypes.h>
|
||||||
#include <llvm/IR/IRBuilder.h>
|
|
||||||
#include <llvm/IR/LLVMContext.h>
|
#include <llvm/IR/LLVMContext.h>
|
||||||
#include <llvm/IR/Module.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;
|
using namespace llvm;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
xe_module_ref module;
|
||||||
|
LLVMContext *context;
|
||||||
|
Module *m;
|
||||||
|
} xe_cpu_module_entry_t;
|
||||||
|
|
||||||
typedef struct xe_cpu {
|
typedef struct xe_cpu {
|
||||||
xe_ref_t ref;
|
xe_ref_t ref;
|
||||||
|
|
||||||
|
@ -24,9 +49,18 @@ typedef struct xe_cpu {
|
||||||
|
|
||||||
xe_pal_ref pal;
|
xe_pal_ref pal;
|
||||||
xe_memory_ref memory;
|
xe_memory_ref memory;
|
||||||
|
|
||||||
|
std::vector<xe_cpu_module_entry_t> entries;
|
||||||
|
|
||||||
|
ExecutionEngine *engine;
|
||||||
} xe_cpu_t;
|
} 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_ref xe_cpu_create(xe_pal_ref pal, xe_memory_ref memory,
|
||||||
xe_cpu_options_t options) {
|
xe_cpu_options_t options) {
|
||||||
xe_cpu_ref cpu = (xe_cpu_ref)xe_calloc(sizeof(xe_cpu));
|
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->pal = xe_pal_retain(pal);
|
||||||
cpu->memory = xe_memory_retain(memory);
|
cpu->memory = xe_memory_retain(memory);
|
||||||
|
|
||||||
|
LLVMLinkInInterpreter();
|
||||||
|
LLVMLinkInJIT();
|
||||||
|
InitializeNativeTarget();
|
||||||
|
XEEXPECTTRUE(llvm_start_multithreaded());
|
||||||
|
|
||||||
return cpu;
|
return cpu;
|
||||||
|
|
||||||
|
XECLEANUP:
|
||||||
|
xe_cpu_release(cpu);
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void xe_cpu_dealloc(xe_cpu_ref cpu) {
|
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_memory_release(cpu->memory);
|
||||||
xe_pal_release(cpu->pal);
|
xe_pal_release(cpu->pal);
|
||||||
}
|
}
|
||||||
|
@ -62,52 +118,163 @@ xe_memory_ref xe_cpu_get_memory(xe_cpu_ref cpu) {
|
||||||
return xe_memory_retain(cpu->memory);
|
return xe_memory_retain(cpu->memory);
|
||||||
}
|
}
|
||||||
|
|
||||||
int xe_cpu_prepare_module(xe_cpu_ref cpu, xe_module_ref module) {
|
int xe_cpu_setup_engine(xe_cpu_ref cpu, Module *gen_module) {
|
||||||
// TODO(benvanik): lookup the module in the cache.
|
if (cpu->engine) {
|
||||||
|
// Engine already initialized - just add the module.
|
||||||
// TODO(benvanik): implement prepare module.
|
cpu->engine->addModule(gen_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;
|
|
||||||
return 0;
|
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) {
|
int xe_cpu_execute(xe_cpu_ref cpu, uint32_t address) {
|
||||||
// TODO(benvanik): implement execute.
|
// TODO(benvanik): implement execute.
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||||
{
|
{
|
||||||
'sources': [
|
'sources': [
|
||||||
|
'codegen.cc',
|
||||||
'cpu.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.
|
// 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).
|
// Stash in modules list (takes reference).
|
||||||
// TODO(benvanik): stash in list.
|
// TODO(benvanik): stash in list.
|
||||||
|
|
|
@ -61,6 +61,7 @@ def discover_commands():
|
||||||
'pull': PullCommand(),
|
'pull': PullCommand(),
|
||||||
'gyp': GypCommand(),
|
'gyp': GypCommand(),
|
||||||
'build': BuildCommand(),
|
'build': BuildCommand(),
|
||||||
|
'xethunk': XethunkCommand(),
|
||||||
'clean': CleanCommand(),
|
'clean': CleanCommand(),
|
||||||
'nuke': NukeCommand(),
|
'nuke': NukeCommand(),
|
||||||
}
|
}
|
||||||
|
@ -379,6 +380,33 @@ class BuildCommand(Command):
|
||||||
return 0
|
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):
|
class CleanCommand(Command):
|
||||||
"""'clean' command."""
|
"""'clean' command."""
|
||||||
|
|
||||||
|
|
|
@ -74,7 +74,7 @@
|
||||||
['_type=="executable"', {
|
['_type=="executable"', {
|
||||||
'libraries': [
|
'libraries': [
|
||||||
'<!@(<(llvm_config) --ldflags)',
|
'<!@(<(llvm_config) --ldflags)',
|
||||||
'<!@(<(llvm_config) --libs core)',
|
'<!@(<(llvm_config) --libs all)',
|
||||||
],
|
],
|
||||||
'library_dirs': [
|
'library_dirs': [
|
||||||
# NOTE: this doesn't actually do anything...
|
# NOTE: this doesn't actually do anything...
|
||||||
|
@ -84,7 +84,7 @@
|
||||||
'xcode_settings': {
|
'xcode_settings': {
|
||||||
'OTHER_LDFLAGS': [
|
'OTHER_LDFLAGS': [
|
||||||
'<!@(<(llvm_config) --ldflags)',
|
'<!@(<(llvm_config) --ldflags)',
|
||||||
'<!@(<(llvm_config) --libs core)',
|
'<!@(<(llvm_config) --libs all)',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
}],
|
}],
|
||||||
|
|
Loading…
Reference in New Issue