Work on CPU, codegen skeleton, and thunk.

This commit is contained in:
Ben Vanik
2013-01-13 15:48:18 -08:00
parent 2f4bc598e5
commit 099e37490a
12 changed files with 557 additions and 46 deletions

View File

@@ -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,52 +118,163 @@ 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.
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;
}
// TODO(benvanik): implement prepare module.
std::string error_message;
cpu->engine = ExecutionEngine::create(gen_module, false, &error_message,
CodeGenOpt::Aggressive);
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;
}
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) {
// TODO(benvanik): implement execute.
return 0;