Now running up to the first required kernel call.

If memory address validation is turned off it runs a lot further.
This commit is contained in:
Ben Vanik
2013-01-28 03:03:37 -08:00
parent 46d5a0b51d
commit 5c2060af72
20 changed files with 342 additions and 91 deletions

View File

@@ -779,12 +779,13 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I
// m <- MASK(MB+32, ME+32)
// RA <- r & m
// The compiler will generate a bunch of these for the special case of
// SH=0, MB=ME
// Which seems to just select a single bit and set cr0 for use with a branch.
// The compiler will generate a bunch of these for the special case of SH=0.
// Which seems to just select some bits and set cr0 for use with a branch.
// We can detect this and do less work.
if (!i.M.SH && i.M.MB == i.M.ME) {
Value* v = b.CreateAnd(g.gpr_value(i.M.RS), 1 << i.M.MB);
if (!i.M.SH) {
Value* v = b.CreateAnd(b.CreateTrunc(g.gpr_value(i.M.RS), b.getInt32Ty()),
b.getInt32(XEMASK(i.M.MB + 32, i.M.ME + 32)));
v = b.CreateZExt(v, b.getInt64Ty());
g.update_gpr_value(i.M.RA, v);
if (i.M.Rc) {
// With cr0 update.
@@ -793,20 +794,20 @@ XEEMITTER(rlwinmx, 0x54000000, M )(FunctionGenerator& g, IRBuilder<>& b, I
return 0;
}
// // ROTL32(x, y) = rotl(i64.(x||x), y)
// Value* v = b.CreateZExt(b.CreateTrunc(g.gpr_value(i.M.RS)), b.getInt64Ty());
// v = b.CreateOr(b.CreateLShr(v, 32), v);
// // (v << shift) | (v >> (64 - shift));
// v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH));
// v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32));
// ROTL32(x, y) = rotl(i64.(x||x), y)
Value* v = b.CreateAnd(g.gpr_value(i.M.RS), UINT32_MAX);
v = b.CreateOr(b.CreateShl(v, 32), v);
// (v << shift) | (v >> (32 - shift));
v = b.CreateOr(b.CreateShl(v, i.M.SH), b.CreateLShr(v, 32 - i.M.SH));
v = b.CreateAnd(v, XEMASK(i.M.MB + 32, i.M.ME + 32));
g.update_gpr_value(i.M.RA, v);
// if (i.M.Rc) {
// // With cr0 update.
// g.update_cr_with_cond(0, v, b.getInt64(0), true);
// }
if (i.M.Rc) {
// With cr0 update.
g.update_cr_with_cond(0, v, b.getInt64(0), true);
}
XEINSTRNOTIMPLEMENTED();
return 1;
return 0;
}
XEEMITTER(rlwnmx, 0x5C000000, M )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {

View File

@@ -90,13 +90,18 @@ int XeEmitBranchTo(
Function* target_fn = g.GetFunction(fn_block->outgoing_function);
Function::arg_iterator args = g.gen_fn()->arg_begin();
Value* state_ptr = args;
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
BasicBlock* next_bb = g.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
// next instruction. This allows the return from our callee to pop
// all the way up.
b.CreateCall2(target_fn, state_ptr, ++args);
b.CreateRetVoid();
} else {
// Will return here eventually.
// Refill registers from state.
b.CreateCall2(target_fn, state_ptr, b.getInt64(cia + 4));
g.FillRegisters();
b.CreateBr(next_bb);
}
@@ -188,7 +193,7 @@ XEEMITTER(bcx, 0x40000000, B )(FunctionGenerator& g, IRBuilder<>& b, I
// Ignore cond.
} else {
Value* cr = g.cr_value(i.B.BI >> 2);
cr = b.CreateAnd(cr, 1 << (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));
} else {
@@ -257,7 +262,7 @@ XEEMITTER(bcctrx, 0x4C000420, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (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));
} else {
@@ -336,7 +341,7 @@ XEEMITTER(bclrx, 0x4C000020, XL )(FunctionGenerator& g, IRBuilder<>& b, I
// Ignore cond.
} else {
Value* cr = g.cr_value(i.XL.BI >> 2);
cr = b.CreateAnd(cr, 1 << (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));
} else {

View File

@@ -9,6 +9,8 @@
#include <xenia/cpu/codegen/function_generator.h>
#include <llvm/IR/Intrinsics.h>
#include <xenia/cpu/ppc/state.h>
#include "cpu/cpu-private.h"
@@ -20,6 +22,10 @@ using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
DEFINE_bool(memory_address_verification, false,
"Whether to add additional checks to generated memory load/stores.");
/**
* This generates function code.
* One context is created for each function to generate. Each basic block in
@@ -752,9 +758,6 @@ void FunctionGenerator::update_cr_with_cond(
// bit1 = RA > RB
// bit2 = RA = RB
// bit3 = XER[SO]
// Bits are reversed:
// 0123
// 3210
// TODO(benvanik): inline this using the x86 cmp instruction - this prevents
// the need for a lot of the compares and ensures we lower to the best
@@ -770,8 +773,8 @@ void FunctionGenerator::update_cr_with_cond(
b.CreateICmpSLT(lhs, rhs) : b.CreateICmpULT(lhs, rhs);
Value* is_gt = is_signed ?
b.CreateICmpSGT(lhs, rhs) : b.CreateICmpUGT(lhs, rhs);
Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 2), b.getInt8(1 << 1));
Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 3), cp);
Value* cp = b.CreateSelect(is_gt, b.getInt8(1 << 1), b.getInt8(1 << 2));
Value* c = b.CreateSelect(is_lt, b.getInt8(1 << 0), cp);
// TODO(benvanik): set bit 4 to XER[SO]
@@ -834,26 +837,53 @@ Value* FunctionGenerator::GetMembase() {
return builder_->CreateLoad(v);
}
Value* FunctionGenerator::memory_addr(uint32_t addr) {
return NULL;
Value* FunctionGenerator::GetMemoryAddress(Value* addr) {
IRBuilder<>& b = *builder_;
// Input address is always in 32-bit space.
addr = b.CreateAnd(addr, UINT_MAX);
// Add runtime memory address checks, if needed.
if (FLAGS_memory_address_verification) {
BasicBlock* invalid_bb = BasicBlock::Create(*context_, "", gen_fn_);
BasicBlock* valid_bb = BasicBlock::Create(*context_, "", gen_fn_);
Value* gt = b.CreateICmpUGE(addr, b.getInt64(0x10000000));
b.CreateCondBr(gt, valid_bb, invalid_bb);
b.SetInsertPoint(invalid_bb);
Function* debugtrap = Intrinsic::getDeclaration(
gen_module_, Intrinsic::debugtrap);
b.CreateCall(debugtrap);
b.CreateBr(valid_bb);
b.SetInsertPoint(valid_bb);
}
// Rebase off of memory base pointer.
return b.CreateInBoundsGEP(GetMembase(), addr);
}
Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) {
IRBuilder<>& b = *builder_;
Type* dataTy = NULL;
bool needs_swap = false;
switch (size) {
case 1:
dataTy = b.getInt8Ty();
break;
case 2:
dataTy = b.getInt16Ty();
needs_swap = true;
break;
case 4:
dataTy = b.getInt32Ty();
needs_swap = true;
break;
case 8:
dataTy = b.getInt64Ty();
needs_swap = true;
break;
default:
XEASSERTALWAYS();
@@ -861,31 +891,41 @@ Value* FunctionGenerator::ReadMemory(Value* addr, uint32_t size, bool extend) {
}
PointerType* pointerTy = PointerType::getUnqual(dataTy);
// Input address is always in 32-bit space.
addr = b.CreateAnd(addr, UINT_MAX);
Value* offset_addr = b.CreateAdd(addr, b.getInt64(size));
Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr);
Value* address = GetMemoryAddress(addr);
Value* ptr = b.CreatePointerCast(address, pointerTy);
return b.CreateLoad(ptr);
Value* value = b.CreateLoad(ptr);
// Swap after loading.
// TODO(benvanik): find a way to avoid this!
if (needs_swap) {
Function* bswap = Intrinsic::getDeclaration(
gen_module_, Intrinsic::bswap, dataTy);
value = b.CreateCall(bswap, value);
}
return value;
}
void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) {
IRBuilder<>& b = *builder_;
Type* dataTy = NULL;
bool needs_swap = false;
switch (size) {
case 1:
dataTy = b.getInt8Ty();
break;
case 2:
dataTy = b.getInt16Ty();
needs_swap = true;
break;
case 4:
dataTy = b.getInt32Ty();
needs_swap = true;
break;
case 8:
dataTy = b.getInt64Ty();
needs_swap = true;
break;
default:
XEASSERTALWAYS();
@@ -893,16 +933,21 @@ void FunctionGenerator::WriteMemory(Value* addr, uint32_t size, Value* value) {
}
PointerType* pointerTy = PointerType::getUnqual(dataTy);
// Input address is always in 32-bit space.
addr = b.CreateAnd(addr, UINT_MAX);
Value* offset_addr = b.CreateAdd(addr, b.getInt64(size));
Value* address = b.CreateInBoundsGEP(GetMembase(), offset_addr);
Value* address = GetMemoryAddress(addr);
Value* ptr = b.CreatePointerCast(address, pointerTy);
// Truncate, if required.
if (value->getType() != dataTy) {
value = b.CreateTrunc(value, dataTy);
}
// Swap before storing.
// TODO(benvanik): find a way to avoid this!
if (needs_swap) {
Function* bswap = Intrinsic::getDeclaration(
gen_module_, Intrinsic::bswap, dataTy);
value = b.CreateCall(bswap, value);
}
b.CreateStore(value, ptr);
}

View File

@@ -102,7 +102,7 @@ int ModuleGenerator::Generate() {
PrepareFunction(fn);
break;
case FunctionSymbol::Kernel:
if (fn->kernel_export && fn->kernel_export->IsImplemented()) {
if (fn->kernel_export && fn->kernel_export->is_implemented) {
AddPresentImport(fn);
} else {
AddMissingImport(fn);
@@ -191,20 +191,19 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name);
// TODO(benvanik): log errors.
BasicBlock* block = BasicBlock::Create(context, "entry", f);
IRBuilder<> builder(block);
IRBuilder<> b(block);
if (FLAGS_trace_kernel_calls) {
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
builder.CreateCall3(
b.CreateCall3(
traceKernelCall,
f->arg_begin(),
builder.getInt64(fn->start_address),
b.getInt64(fn->start_address),
++f->arg_begin());
}
builder.CreateRetVoid();
b.CreateRetVoid();
OptimizeFunction(m, f);
@@ -219,10 +218,44 @@ void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
}
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
// Module *m = gen_module_;
// LLVMContext& context = m->getContext();
Module *m = gen_module_;
LLVMContext& context = m->getContext();
// TODO(benvanik): add import thunk code.
// Add the extern.
char shim_name[256];
xesnprintfa(shim_name, XECOUNT(shim_name),
"__shim__%s", fn->kernel_export->name);
std::vector<Type*> shimArgs;
shimArgs.push_back(PointerType::getUnqual(Type::getInt8Ty(context)));
FunctionType* shimTy = FunctionType::get(
Type::getVoidTy(context), shimArgs, false);
Function* shim = Function::Create(
shimTy, Function::ExternalLinkage, shim_name, m);
// engine_->addGlobalMapping(shim,
// (void*)fn->kernel_export->function_data.shim);
// Create the function (and setup args/attributes/etc).
Function* f = CreateFunctionDefinition(fn->name);
BasicBlock* block = BasicBlock::Create(context, "entry", f);
IRBuilder<> b(block);
if (FLAGS_trace_kernel_calls) {
Value* traceKernelCall = m->getFunction("XeTraceKernelCall");
b.CreateCall3(
traceKernelCall,
f->arg_begin(),
b.getInt64(fn->start_address),
++f->arg_begin());
}
b.CreateCall(
shim,
f->arg_begin());
b.CreateRetVoid();
OptimizeFunction(m, f);
}
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {

View File

@@ -41,6 +41,7 @@ using namespace llvm;
using namespace xe;
using namespace xe::cpu;
using namespace xe::cpu::codegen;
using namespace xe::cpu::sdb;
using namespace xe::kernel;
@@ -72,7 +73,16 @@ int ExecModule::PrepareUserModule(kernel::UserModule* user_module) {
sdb_ = shared_ptr<sdb::SymbolDatabase>(
new sdb::XexSymbolDatabase(memory_, export_resolver_.get(), user_module));
return Prepare();
int result_code = Prepare();
if (result_code) {
return result_code;
}
// Import variables.
xe_xex2_ref xex = user_module->xex();
xe_xex2_release(xex);
return 0;
}
int ExecModule::PrepareRawBinary(uint32_t start_address, uint32_t end_address) {
@@ -339,6 +349,38 @@ int ExecModule::InjectGlobals() {
}
int ExecModule::Init() {
// Setup all kernel variables.
std::vector<VariableSymbol*> variables;
if (sdb_->GetAllVariables(variables)) {
return 1;
}
uint8_t* mem = xe_memory_addr(memory_, 0);
for (std::vector<VariableSymbol*>::iterator it = variables.begin();
it != variables.end(); ++it) {
VariableSymbol* var = *it;
if (!var->kernel_export) {
continue;
}
KernelExport* kernel_export = var->kernel_export;
// Grab, if available.
uint32_t* slot = (uint32_t*)(mem + var->address);
if (kernel_export->type == KernelExport::Function) {
// Not exactly sure what this should be...
// TODO(benvanik): find out what import variables are.
} else {
if (kernel_export->is_implemented) {
// Implemented - replace with pointer.
*slot = kernel_export->variable_ptr;
} else {
// Not implemented - write with a dummy value.
*slot = 0xDEADBEEF;
XELOGCPU("WARNING: imported a variable with no value: %s",
kernel_export->name);
}
}
}
// Run static initializers. I'm not sure we'll have any, but who knows.
engine_->runStaticConstructorsDestructors(gen_module_.get(), false);

View File

@@ -43,6 +43,8 @@ namespace {
LLVMLinkInJIT();
InitializeNativeTarget();
llvm_start_multithreaded();
// TODO(benvanik): only do this once
codegen::RegisterEmitCategoryALU();
codegen::RegisterEmitCategoryControl();
@@ -89,17 +91,21 @@ xe_memory_ref Processor::memory() {
int Processor::Setup() {
XEASSERTNULL(engine_);
if (!llvm_start_multithreaded()) {
return 1;
}
dummy_context_ = auto_ptr<LLVMContext>(new LLVMContext());
Module* dummy_module = new Module("dummy", *dummy_context_.get());
std::string error_message;
engine_ = shared_ptr<ExecutionEngine>(
ExecutionEngine::create(dummy_module, false, &error_message,
CodeGenOpt::Aggressive, false));
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_) {
return 1;
}
@@ -178,7 +184,7 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
uint32_t lr = 0xBEBEBEBE;
// Setup registers.
ppc_state->lr = 0xBEBEBEBE;
ppc_state->lr = lr;
// Args:
// - i8* state
@@ -191,6 +197,10 @@ int Processor::Execute(ThreadState* thread_state, uint32_t address) {
GenericValue ret = engine_->runFunction(f, args);
// typedef void (*fnptr)(xe_ppc_state_t*, uint64_t);
// fnptr ptr = (fnptr)engine_->getPointerToFunction(f);
// ptr(ppc_state, lr);
//return (uint32_t)ret.IntVal.getSExtValue();
return 0;
}

View File

@@ -84,7 +84,8 @@ FunctionBlock* FunctionSymbol::SplitBlock(uint32_t address) {
VariableSymbol::VariableSymbol() :
Symbol(Variable),
address(0), name(0) {
address(0), name(0),
kernel_export(0) {
}
VariableSymbol::~VariableSymbol() {
@@ -213,6 +214,15 @@ VariableSymbol* SymbolDatabase::GetVariable(uint32_t address) {
return NULL;
}
int SymbolDatabase::GetAllVariables(std::vector<VariableSymbol*>& variables) {
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
if (it->second->symbol_type == Symbol::Variable) {
variables.push_back(static_cast<VariableSymbol*>(it->second));
}
}
return 0;
}
int SymbolDatabase::GetAllFunctions(vector<FunctionSymbol*>& functions) {
for (SymbolMap::iterator it = symbols_.begin(); it != symbols_.end(); ++it) {
if (it->second->symbol_type == Symbol::Function) {
@@ -847,6 +857,7 @@ int XexSymbolDatabase::AddImports(const xe_xex2_import_library_t* library) {
info->ordinal);
}
var->name = xestrdupa(name);
var->kernel_export = kernel_export;
if (info->thunk_address) {
FunctionSymbol* fn = GetOrInsertFunction(info->thunk_address);
fn->end_address = fn->start_address + 16 - 4;