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:
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user