Work on CPU, codegen skeleton, and thunk.
This commit is contained in:
251
src/cpu/cpu.cc
251
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,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;
|
||||
|
||||
Reference in New Issue
Block a user