Removing llvm backend.
This commit is contained in:
parent
eb22289dc7
commit
edb3aabdd4
|
@ -1,41 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_EMIT_H_
|
||||
#define XENIA_CPU_LLVMBE_EMIT_H_
|
||||
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
void RegisterEmitCategoryALU();
|
||||
void RegisterEmitCategoryControl();
|
||||
void RegisterEmitCategoryFPU();
|
||||
void RegisterEmitCategoryMemory();
|
||||
|
||||
|
||||
#define XEEMITTER(name, opcode, format) int InstrEmit_##name
|
||||
|
||||
#define XEREGISTERINSTR(name, opcode) \
|
||||
RegisterInstrEmit(opcode, (InstrEmitFn)InstrEmit_##name);
|
||||
|
||||
#define XEINSTRNOTIMPLEMENTED()
|
||||
//#define XEINSTRNOTIMPLEMENTED XEASSERTALWAYS
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_EMIT_H_
|
File diff suppressed because it is too large
Load Diff
|
@ -1,713 +0,0 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/emit.h>
|
||||
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
#include <xenia/cpu/ppc/state.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
int XeEmitIndirectBranchTo(
|
||||
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk, uint32_t reg) {
|
||||
// TODO(benvanik): run a DFA pass to see if we can detect whether this is
|
||||
// a normal function return that is pulling the LR from the stack that
|
||||
// it set in the prolog. If so, we can omit the dynamic check!
|
||||
|
||||
// NOTE: we avoid spilling registers until we know that the target is not
|
||||
// a basic block within this function.
|
||||
|
||||
Value* target;
|
||||
switch (reg) {
|
||||
case kXEPPCRegLR:
|
||||
target = e.lr_value();
|
||||
break;
|
||||
case kXEPPCRegCTR:
|
||||
target = e.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Dynamic test when branching to LR, which is usually used for the return.
|
||||
// We only do this if LK=0 as returns wouldn't set LR.
|
||||
// Ideally it's a return and we can just do a simple ret and be done.
|
||||
// If it's not, we fall through to the full indirection logic.
|
||||
if (!lk && reg == kXEPPCRegLR) {
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
BasicBlock* mismatch_bb = BasicBlock::Create(*e.context(), "lr_mismatch",
|
||||
e.gen_fn(), next_block);
|
||||
Value* lr_cmp = b.CreateICmpEQ(target, ++(e.gen_fn()->arg_begin()));
|
||||
// The return block will spill registers for us.
|
||||
b.CreateCondBr(lr_cmp, e.GetReturnBasicBlock(), mismatch_bb);
|
||||
b.SetInsertPoint(mismatch_bb);
|
||||
}
|
||||
|
||||
// Defer to the generator, which will do fancy things.
|
||||
bool likely_local = !lk && reg == kXEPPCRegCTR;
|
||||
return e.GenerateIndirectionBranch(cia, target, lk, likely_local);
|
||||
}
|
||||
|
||||
int XeEmitBranchTo(
|
||||
EmitterContext& e, IRBuilder<>& b, const char* src, uint32_t cia,
|
||||
bool lk) {
|
||||
// Get the basic block and switch behavior based on outgoing type.
|
||||
FunctionBlock* fn_block = e.fn_block();
|
||||
switch (fn_block->outgoing_type) {
|
||||
case FunctionBlock::kTargetBlock:
|
||||
{
|
||||
BasicBlock* target_bb = e.GetBasicBlock(fn_block->outgoing_address);
|
||||
XEASSERTNOTNULL(target_bb);
|
||||
b.CreateBr(target_bb);
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetFunction:
|
||||
{
|
||||
// Spill all registers to memory.
|
||||
// TODO(benvanik): only spill ones used by the target function? Use
|
||||
// calling convention flags on the function to not spill temp
|
||||
// registers?
|
||||
e.SpillRegisters();
|
||||
|
||||
XEASSERTNOTNULL(fn_block->outgoing_function);
|
||||
Function* target_fn = e.GetFunction(fn_block->outgoing_function);
|
||||
Function::arg_iterator args = e.gen_fn()->arg_begin();
|
||||
Value* state_ptr = args;
|
||||
BasicBlock* next_bb = e.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));
|
||||
e.FillRegisters();
|
||||
b.CreateBr(next_bb);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case FunctionBlock::kTargetLR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA LR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegLR);
|
||||
}
|
||||
case FunctionBlock::kTargetCTR:
|
||||
{
|
||||
// An indirect jump.
|
||||
printf("INDIRECT JUMP VIA CTR: %.8X\n", cia);
|
||||
return XeEmitIndirectBranchTo(e, b, src, cia, lk, kXEPPCRegCTR);
|
||||
}
|
||||
default:
|
||||
case FunctionBlock::kTargetNone:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
XEEMITTER(bx, 0x48000000, I )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if AA then
|
||||
// NIA <- EXTS(LI || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(LI || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
uint32_t nia;
|
||||
if (i.I.AA) {
|
||||
nia = XEEXTS26(i.I.LI << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.I.LI << 2);
|
||||
}
|
||||
if (i.I.LK) {
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
return XeEmitBranchTo(e, b, "bx", i.address, i.I.LK);
|
||||
}
|
||||
|
||||
XEEMITTER(bcx, 0x40000000, B )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3])
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// if AA then
|
||||
// NIA <- EXTS(BD || 0b00)
|
||||
// else
|
||||
// NIA <- CIA + EXTS(BD || 0b00)
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.B.LK) {
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (XESELECTBITS(i.B.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = e.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
e.update_ctr_value(ctr);
|
||||
|
||||
// Ctr check.
|
||||
if (XESELECTBITS(i.B.BO, 1, 1)) {
|
||||
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
|
||||
} else {
|
||||
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.B.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = e.cr_value(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 {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (ctr_ok && cond_ok) {
|
||||
ok = b.CreateAnd(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address);
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
uint32_t nia;
|
||||
if (i.B.AA) {
|
||||
nia = XEEXTS26(i.B.BD << 2);
|
||||
} else {
|
||||
nia = i.address + XEEXTS26(i.B.BD << 2);
|
||||
}
|
||||
if (XeEmitBranchTo(e, b, "bcx", i.address, i.B.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bcctrx, 0x4C000420, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if cond_ok then
|
||||
// NIA <- CTR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = e.cr_value(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 {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcctrx", i.address);
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(e, b, "bcctrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(bclrx, 0x4C000020, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if ¬BO[2] then
|
||||
// CTR <- CTR - 1
|
||||
// ctr_ok <- BO[2] | ((CTR[0:63] != 0) XOR BO[3]
|
||||
// cond_ok <- BO[0] | (CR[BI+32] ≡ BO[1])
|
||||
// if ctr_ok & cond_ok then
|
||||
// NIA <- LR[0:61] || 0b00
|
||||
// if LK then
|
||||
// LR <- CIA + 4
|
||||
|
||||
// NOTE: the condition bits are reversed!
|
||||
// 01234 (docs)
|
||||
// 43210 (real)
|
||||
|
||||
// TODO(benvanik): this may be wrong and overwrite LRs when not desired!
|
||||
// The docs say always, though...
|
||||
if (i.XL.LK) {
|
||||
e.update_lr_value(b.getInt32(i.address + 4));
|
||||
}
|
||||
|
||||
Value* ctr_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 2, 2)) {
|
||||
// Ignore ctr.
|
||||
} else {
|
||||
// Decrement counter.
|
||||
Value* ctr = e.ctr_value();
|
||||
ctr = b.CreateSub(ctr, b.getInt64(1));
|
||||
|
||||
// Ctr check.
|
||||
if (XESELECTBITS(i.XL.BO, 1, 1)) {
|
||||
ctr_ok = b.CreateICmpEQ(ctr, b.getInt64(0));
|
||||
} else {
|
||||
ctr_ok = b.CreateICmpNE(ctr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
Value* cond_ok = NULL;
|
||||
if (XESELECTBITS(i.XL.BO, 4, 4)) {
|
||||
// Ignore cond.
|
||||
} else {
|
||||
Value* cr = e.cr_value(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 {
|
||||
cond_ok = b.CreateICmpEQ(cr, b.getInt64(0));
|
||||
}
|
||||
}
|
||||
|
||||
// We do a bit of optimization here to make the llvm assembly easier to read.
|
||||
Value* ok = NULL;
|
||||
if (ctr_ok && cond_ok) {
|
||||
ok = b.CreateAnd(ctr_ok, cond_ok);
|
||||
} else if (ctr_ok) {
|
||||
ok = ctr_ok;
|
||||
} else if (cond_ok) {
|
||||
ok = cond_ok;
|
||||
}
|
||||
|
||||
// Handle unconditional branches without extra fluff.
|
||||
BasicBlock* original_bb = b.GetInsertBlock();
|
||||
if (ok) {
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X_bclrx", i.address);
|
||||
BasicBlock* next_block = e.GetNextBasicBlock();
|
||||
XEASSERTNOTNULL(next_block);
|
||||
BasicBlock* branch_bb = BasicBlock::Create(*e.context(), name, e.gen_fn(),
|
||||
next_block);
|
||||
|
||||
b.CreateCondBr(ok, branch_bb, next_block);
|
||||
b.SetInsertPoint(branch_bb);
|
||||
}
|
||||
|
||||
// Note that this occurs entirely within the branch true block.
|
||||
if (XeEmitBranchTo(e, b, "bclrx", i.address, i.XL.LK)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
b.SetInsertPoint(original_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// Condition register logical (A-23)
|
||||
|
||||
XEEMITTER(crand, 0x4C000202, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crandc, 0x4C000102, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(creqv, 0x4C000242, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnand, 0x4C0001C2, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crnor, 0x4C000042, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(cror, 0x4C000382, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crorc, 0x4C000342, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(crxor, 0x4C000182, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mcrf, 0x4C000000, XL )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// System linkage (A-24)
|
||||
|
||||
XEEMITTER(sc, 0x44000002, SC )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Trap (A-25)
|
||||
|
||||
int XeEmitTrap(EmitterContext& e, IRBuilder<>& b, InstrData& i,
|
||||
Value* va, Value* vb, uint32_t TO) {
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
// Bits swapped:
|
||||
// 01234
|
||||
// 43210
|
||||
|
||||
if (!TO) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||
e.GetNextBasicBlock());
|
||||
BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.gen_fn(),
|
||||
after_bb);
|
||||
|
||||
// Create the basic blocks (so we can chain).
|
||||
std::vector<BasicBlock*> bbs;
|
||||
if (TO & (1 << 4)) {
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
bbs.push_back(BasicBlock::Create(*e.context(), "", e.gen_fn(), trap_bb));
|
||||
}
|
||||
bbs.push_back(after_bb);
|
||||
|
||||
// Jump to the first bb.
|
||||
b.CreateBr(bbs.front());
|
||||
|
||||
// Setup each basic block.
|
||||
std::vector<BasicBlock*>::iterator it = bbs.begin();
|
||||
if (TO & (1 << 4)) {
|
||||
// a < b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpSLT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 3)) {
|
||||
// a > b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpSGT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 2)) {
|
||||
// a = b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpEQ(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 1)) {
|
||||
// a <u b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpULT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
if (TO & (1 << 0)) {
|
||||
// a >u b
|
||||
BasicBlock* bb = *(it++);
|
||||
b.SetInsertPoint(bb);
|
||||
Value* cmp = b.CreateICmpUGT(va, vb);
|
||||
b.CreateCondBr(cmp, trap_bb, *it);
|
||||
}
|
||||
|
||||
// Create trap BB.
|
||||
b.SetInsertPoint(trap_bb);
|
||||
e.SpillRegisters();
|
||||
// TODO(benvanik): use @llvm.debugtrap? could make debugging better
|
||||
b.CreateCall2(e.gen_module()->getFunction("XeTrap"),
|
||||
e.gen_fn()->arg_begin(),
|
||||
b.getInt32(i.address));
|
||||
b.CreateBr(after_bb);
|
||||
|
||||
// Resume.
|
||||
b.SetInsertPoint(after_bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(td, 0x7C000088, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// b <- (RB)
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(e, b, i,
|
||||
e.gpr_value(i.X.RA),
|
||||
e.gpr_value(i.X.RB),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tdi, 0x08000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- (RA)
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(e, b, i,
|
||||
e.gpr_value(i.D.RA),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
i.D.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(tw, 0x7C000008, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// b <- EXTS((RB)[32:63])
|
||||
// if (a < b) & TO[0] then TRAP
|
||||
// if (a > b) & TO[1] then TRAP
|
||||
// if (a = b) & TO[2] then TRAP
|
||||
// if (a <u b) & TO[3] then TRAP
|
||||
// if (a >u b) & TO[4] then TRAP
|
||||
return XeEmitTrap(e, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RB),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
i.X.RT);
|
||||
}
|
||||
|
||||
XEEMITTER(twi, 0x0C000000, D )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// a <- EXTS((RA)[32:63])
|
||||
// if (a < EXTS(SI)) & TO[0] then TRAP
|
||||
// if (a > EXTS(SI)) & TO[1] then TRAP
|
||||
// if (a = EXTS(SI)) & TO[2] then TRAP
|
||||
// if (a <u EXTS(SI)) & TO[3] then TRAP
|
||||
// if (a >u EXTS(SI)) & TO[4] then TRAP
|
||||
return XeEmitTrap(e, b, i,
|
||||
b.CreateSExt(b.CreateTrunc(e.gpr_value(i.D.RA),
|
||||
b.getInt32Ty()),
|
||||
b.getInt64Ty()),
|
||||
b.getInt64(XEEXTS16(i.D.DS)),
|
||||
i.D.RT);
|
||||
}
|
||||
|
||||
|
||||
// Processor control (A-26)
|
||||
|
||||
XEEMITTER(mfcr, 0x7C000026, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mfspr, 0x7C0002A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// RT <- SPR(n)
|
||||
// else
|
||||
// RT <- i32.0 || SPR(n)
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
Value* v = NULL;
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
v = e.xer_value();
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
v = e.lr_value();
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
v = e.ctr_value();
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
e.update_gpr_value(i.XFX.RT, v);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
XEEMITTER(mftb, 0x7C0002E6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtcrf, 0x7C000120, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtspr, 0x7C0003A6, XFX)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// n <- spr[5:9] || spr[0:4]
|
||||
// if length(SPR(n)) = 64 then
|
||||
// SPR(n) <- (RS)
|
||||
// else
|
||||
// SPR(n) <- (RS)[32:63]
|
||||
|
||||
Value* v = e.gpr_value(i.XFX.RT);
|
||||
|
||||
const uint32_t n = ((i.XFX.spr & 0x1F) << 5) | ((i.XFX.spr >> 5) & 0x1F);
|
||||
switch (n) {
|
||||
case 1:
|
||||
// XER
|
||||
e.update_xer_value(v);
|
||||
break;
|
||||
case 8:
|
||||
// LR
|
||||
e.update_lr_value(v);
|
||||
break;
|
||||
case 9:
|
||||
// CTR
|
||||
e.update_ctr_value(v);
|
||||
break;
|
||||
default:
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void RegisterEmitCategoryControl() {
|
||||
XEREGISTERINSTR(bx, 0x48000000);
|
||||
XEREGISTERINSTR(bcx, 0x40000000);
|
||||
XEREGISTERINSTR(bcctrx, 0x4C000420);
|
||||
XEREGISTERINSTR(bclrx, 0x4C000020);
|
||||
XEREGISTERINSTR(crand, 0x4C000202);
|
||||
XEREGISTERINSTR(crandc, 0x4C000102);
|
||||
XEREGISTERINSTR(creqv, 0x4C000242);
|
||||
XEREGISTERINSTR(crnand, 0x4C0001C2);
|
||||
XEREGISTERINSTR(crnor, 0x4C000042);
|
||||
XEREGISTERINSTR(cror, 0x4C000382);
|
||||
XEREGISTERINSTR(crorc, 0x4C000342);
|
||||
XEREGISTERINSTR(crxor, 0x4C000182);
|
||||
XEREGISTERINSTR(mcrf, 0x4C000000);
|
||||
XEREGISTERINSTR(sc, 0x44000002);
|
||||
XEREGISTERINSTR(td, 0x7C000088);
|
||||
XEREGISTERINSTR(tdi, 0x08000000);
|
||||
XEREGISTERINSTR(tw, 0x7C000008);
|
||||
XEREGISTERINSTR(twi, 0x0C000000);
|
||||
XEREGISTERINSTR(mfcr, 0x7C000026);
|
||||
XEREGISTERINSTR(mfspr, 0x7C0002A6);
|
||||
XEREGISTERINSTR(mftb, 0x7C0002E6);
|
||||
XEREGISTERINSTR(mtcrf, 0x7C000120);
|
||||
XEREGISTERINSTR(mtspr, 0x7C0003A6);
|
||||
}
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
|
@ -1,296 +0,0 @@
|
|||
/*
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/emit.h>
|
||||
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::ppc;
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
// Floating-point arithmetic (A-8)
|
||||
|
||||
XEEMITTER(faddx, 0xFC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(faddsx, 0xEC00002A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivx, 0xFC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fdivsx, 0xEC000024, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulx, 0xFC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmulsx, 0xEC000032, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fresx, 0xEC000030, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frsqrtex, 0xFC000034, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubx, 0xFC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsubsx, 0xEC000028, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fselx, 0xFC00002E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtx, 0xFC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fsqrtsx, 0xEC00002C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point multiply-add (A-9)
|
||||
|
||||
XEEMITTER(fmaddx, 0xFC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmaddsx, 0xEC00003A, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubx, 0xFC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmsubsx, 0xEC000038, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddx, 0xFC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmaddsx, 0xEC00003E, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubx, 0xFC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnmsubsx, 0xEC00003C, A )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point rounding and conversion (A-10)
|
||||
|
||||
XEEMITTER(fcfidx, 0xFC00069C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidx, 0xFC00065C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctidzx, 0xFC00065E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwx, 0xFC00001C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fctiwzx, 0xFC00001E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(frspx, 0xFC000018, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point compare (A-11)
|
||||
|
||||
XEEMITTER(fcmpo, 0xFC000040, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fcmpu, 0xFC000000, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
// if (FRA) is a NaN or (FRB) is a NaN then
|
||||
// c <- 0b0001
|
||||
// else if (FRA) < (FRB) then
|
||||
// c <- 0b1000
|
||||
// else if (FRA) > (FRB) then
|
||||
// c <- 0b0100
|
||||
// else {
|
||||
// c <- 0b0010
|
||||
// }
|
||||
// FPCC <- c
|
||||
// CR[4*BF:4*BF+3] <- c
|
||||
// if (FRA) is an SNaN or (FRB) is an SNaN then
|
||||
// VXSNAN <- 1
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point status and control register (A
|
||||
|
||||
XEEMITTER(mcrfs, 0xFC000080, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mffsx, 0xFC00048E, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb0x, 0xFC00008C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsb1x, 0xFC00004C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfx, 0xFC00058E, XFL)(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(mtfsfix, 0xFC00010C, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Floating-point move (A-21)
|
||||
|
||||
XEEMITTER(fabsx, 0xFC000210, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fmrx, 0xFC000090, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnabsx, 0xFC000110, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
XEEMITTER(fnegx, 0xFC000050, X )(EmitterContext& e, IRBuilder<>& b, InstrData& i) {
|
||||
XEINSTRNOTIMPLEMENTED();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
void RegisterEmitCategoryFPU() {
|
||||
XEREGISTERINSTR(faddx, 0xFC00002A);
|
||||
XEREGISTERINSTR(faddsx, 0xEC00002A);
|
||||
XEREGISTERINSTR(fdivx, 0xFC000024);
|
||||
XEREGISTERINSTR(fdivsx, 0xEC000024);
|
||||
XEREGISTERINSTR(fmulx, 0xFC000032);
|
||||
XEREGISTERINSTR(fmulsx, 0xEC000032);
|
||||
XEREGISTERINSTR(fresx, 0xEC000030);
|
||||
XEREGISTERINSTR(frsqrtex, 0xFC000034);
|
||||
XEREGISTERINSTR(fsubx, 0xFC000028);
|
||||
XEREGISTERINSTR(fsubsx, 0xEC000028);
|
||||
XEREGISTERINSTR(fselx, 0xFC00002E);
|
||||
XEREGISTERINSTR(fsqrtx, 0xFC00002C);
|
||||
XEREGISTERINSTR(fsqrtsx, 0xEC00002C);
|
||||
XEREGISTERINSTR(fmaddx, 0xFC00003A);
|
||||
XEREGISTERINSTR(fmaddsx, 0xEC00003A);
|
||||
XEREGISTERINSTR(fmsubx, 0xFC000038);
|
||||
XEREGISTERINSTR(fmsubsx, 0xEC000038);
|
||||
XEREGISTERINSTR(fnmaddx, 0xFC00003E);
|
||||
XEREGISTERINSTR(fnmaddsx, 0xEC00003E);
|
||||
XEREGISTERINSTR(fnmsubx, 0xFC00003C);
|
||||
XEREGISTERINSTR(fnmsubsx, 0xEC00003C);
|
||||
XEREGISTERINSTR(fcfidx, 0xFC00069C);
|
||||
XEREGISTERINSTR(fctidx, 0xFC00065C);
|
||||
XEREGISTERINSTR(fctidzx, 0xFC00065E);
|
||||
XEREGISTERINSTR(fctiwx, 0xFC00001C);
|
||||
XEREGISTERINSTR(fctiwzx, 0xFC00001E);
|
||||
XEREGISTERINSTR(frspx, 0xFC000018);
|
||||
XEREGISTERINSTR(fcmpo, 0xFC000040);
|
||||
XEREGISTERINSTR(fcmpu, 0xFC000000);
|
||||
XEREGISTERINSTR(mcrfs, 0xFC000080);
|
||||
XEREGISTERINSTR(mffsx, 0xFC00048E);
|
||||
XEREGISTERINSTR(mtfsb0x, 0xFC00008C);
|
||||
XEREGISTERINSTR(mtfsb1x, 0xFC00004C);
|
||||
XEREGISTERINSTR(mtfsfx, 0xFC00058E);
|
||||
XEREGISTERINSTR(mtfsfix, 0xFC00010C);
|
||||
XEREGISTERINSTR(fabsx, 0xFC000210);
|
||||
XEREGISTERINSTR(fmrx, 0xFC000090);
|
||||
XEREGISTERINSTR(fnabsx, 0xFC000110);
|
||||
XEREGISTERINSTR(fnegx, 0xFC000050);
|
||||
}
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,146 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_EMITTER_CONTEXT_H_
|
||||
#define XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
||||
|
||||
#include <llvm/IR/Attributes.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 <xenia/cpu/sdb.h>
|
||||
#include <xenia/cpu/ppc/instr.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class EmitterContext {
|
||||
public:
|
||||
EmitterContext(
|
||||
xe_memory_ref memory,
|
||||
llvm::LLVMContext* context, llvm::Module* gen_module);
|
||||
~EmitterContext();
|
||||
|
||||
int Init(sdb::FunctionSymbol* fn, llvm::Function* gen_fn);
|
||||
|
||||
llvm::LLVMContext* context();
|
||||
llvm::Module* gen_module();
|
||||
|
||||
sdb::FunctionSymbol* fn();
|
||||
llvm::Function* gen_fn();
|
||||
sdb::FunctionBlock* fn_block();
|
||||
|
||||
void PushInsertPoint();
|
||||
void PopInsertPoint();
|
||||
|
||||
void GenerateBasicBlocks();
|
||||
llvm::BasicBlock* GetBasicBlock(uint32_t address);
|
||||
llvm::BasicBlock* GetNextBasicBlock();
|
||||
llvm::BasicBlock* GetReturnBasicBlock();
|
||||
|
||||
llvm::Function* GetFunction(sdb::FunctionSymbol* symbol);
|
||||
|
||||
int GenerateIndirectionBranch(uint32_t cia, llvm::Value* target,
|
||||
bool lk, bool likely_local);
|
||||
|
||||
llvm::Value* LoadStateValue(uint32_t offset, llvm::Type* type,
|
||||
const char* name = "");
|
||||
void StoreStateValue(uint32_t offset, llvm::Type* type, llvm::Value* value);
|
||||
|
||||
llvm::Value* cia_value();
|
||||
|
||||
llvm::Value* SetupLocal(llvm::Type* type, const char* name);
|
||||
void FillRegisters();
|
||||
void SpillRegisters();
|
||||
|
||||
llvm::Value* xer_value();
|
||||
void update_xer_value(llvm::Value* value);
|
||||
void update_xer_with_overflow(llvm::Value* value);
|
||||
void update_xer_with_carry(llvm::Value* value);
|
||||
void update_xer_with_overflow_and_carry(llvm::Value* value);
|
||||
|
||||
llvm::Value* lr_value();
|
||||
void update_lr_value(llvm::Value* value);
|
||||
|
||||
llvm::Value* ctr_value();
|
||||
void update_ctr_value(llvm::Value* value);
|
||||
|
||||
llvm::Value* cr_value(uint32_t n);
|
||||
void update_cr_value(uint32_t n, llvm::Value* value);
|
||||
void update_cr_with_cond(uint32_t n, llvm::Value* lhs, llvm::Value* rhs,
|
||||
bool is_signed);
|
||||
|
||||
llvm::Value* gpr_value(uint32_t n);
|
||||
void update_gpr_value(uint32_t n, llvm::Value* value);
|
||||
llvm::Value* fpr_value(uint32_t n);
|
||||
void update_fpr_value(uint32_t n, llvm::Value* value);
|
||||
|
||||
llvm::Value* GetMembase();
|
||||
llvm::Value* GetMemoryAddress(uint32_t cia, llvm::Value* addr);
|
||||
llvm::Value* ReadMemory(
|
||||
uint32_t cia, llvm::Value* addr, uint32_t size, bool acquire = false);
|
||||
void WriteMemory(
|
||||
uint32_t cia, llvm::Value* addr, uint32_t size, llvm::Value* value,
|
||||
bool release = false);
|
||||
|
||||
private:
|
||||
void GenerateSharedBlocks();
|
||||
int PrepareBasicBlock(sdb::FunctionBlock* block);
|
||||
void GenerateBasicBlock(sdb::FunctionBlock* block);
|
||||
void SetupLocals();
|
||||
|
||||
xe_memory_ref memory_;
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* gen_module_;
|
||||
llvm::IRBuilder<>* builder_;
|
||||
llvm::FunctionType* fn_type_;
|
||||
|
||||
sdb::FunctionSymbol* fn_;
|
||||
llvm::Function* gen_fn_;
|
||||
sdb::FunctionBlock* fn_block_;
|
||||
llvm::BasicBlock* return_block_;
|
||||
llvm::BasicBlock* internal_indirection_block_;
|
||||
llvm::BasicBlock* external_indirection_block_;
|
||||
llvm::BasicBlock* bb_;
|
||||
|
||||
std::vector<std::pair<llvm::BasicBlock*, llvm::BasicBlock::iterator> >
|
||||
insert_points_;
|
||||
|
||||
std::map<uint32_t, llvm::BasicBlock*> bbs_;
|
||||
|
||||
// Address of the instruction being generated.
|
||||
uint32_t cia_;
|
||||
|
||||
ppc::InstrAccessBits access_bits_;
|
||||
struct {
|
||||
llvm::Value* indirection_target;
|
||||
llvm::Value* indirection_cia;
|
||||
|
||||
llvm::Value* xer;
|
||||
llvm::Value* lr;
|
||||
llvm::Value* ctr;
|
||||
llvm::Value* cr[8];
|
||||
llvm::Value* gpr[32];
|
||||
llvm::Value* fpr[32];
|
||||
} locals_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_EMITTER_CONTEXT_H_
|
|
@ -1,78 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_backend.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/Interpreter.h>
|
||||
#include <llvm/ExecutionEngine/JIT.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Support/ManagedStatic.h>
|
||||
#include <llvm/Support/TargetSelect.h>
|
||||
|
||||
#include <xenia/cpu/llvmbe/emit.h>
|
||||
#include <xenia/cpu/llvmbe/llvm_jit.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
namespace {
|
||||
void InitializeIfNeeded();
|
||||
void CleanupOnShutdown();
|
||||
|
||||
void InitializeIfNeeded() {
|
||||
static bool has_initialized = false;
|
||||
if (has_initialized) {
|
||||
return;
|
||||
}
|
||||
has_initialized = true;
|
||||
|
||||
LLVMLinkInInterpreter();
|
||||
LLVMLinkInJIT();
|
||||
InitializeNativeTarget();
|
||||
|
||||
llvm_start_multithreaded();
|
||||
|
||||
llvmbe::RegisterEmitCategoryALU();
|
||||
llvmbe::RegisterEmitCategoryControl();
|
||||
llvmbe::RegisterEmitCategoryFPU();
|
||||
llvmbe::RegisterEmitCategoryMemory();
|
||||
|
||||
atexit(CleanupOnShutdown);
|
||||
}
|
||||
|
||||
void CleanupOnShutdown() {
|
||||
llvm_shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
LLVMBackend::LLVMBackend() :
|
||||
Backend() {
|
||||
InitializeIfNeeded();
|
||||
}
|
||||
|
||||
LLVMBackend::~LLVMBackend() {
|
||||
}
|
||||
|
||||
CodeUnitBuilder* LLVMBackend::CreateCodeUnitBuilder() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LibraryLoader* LLVMBackend::CreateLibraryLoader() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
JIT* LLVMBackend::CreateJIT(xe_memory_ref memory, FunctionTable* fn_table) {
|
||||
return new LLVMJIT(memory, fn_table);
|
||||
}
|
|
@ -1,43 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_BACKEND_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_BACKEND_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
|
||||
#include <xenia/cpu/backend.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMBackend : public Backend {
|
||||
public:
|
||||
LLVMBackend();
|
||||
virtual ~LLVMBackend();
|
||||
|
||||
virtual CodeUnitBuilder* CreateCodeUnitBuilder();
|
||||
virtual LibraryLinker* CreateLibraryLinker();
|
||||
virtual LibraryLoader* CreateLibraryLoader();
|
||||
|
||||
virtual JIT* CreateJIT(xe_memory_ref memory, FunctionTable* fn_table);
|
||||
|
||||
protected:
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_BACKEND_H_
|
|
@ -1,264 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_code_unit_builder.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/DebugInfo.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Attributes.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/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/cpu-private.h>
|
||||
#include <xenia/cpu/llvmbe/emitter_context.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
LLVMCodeUnitBuilder::LLVMCodeUnitBuilder(
|
||||
xe_memory_ref memory, LLVMContext* context) :
|
||||
CodeUnitBuilder(memory),
|
||||
context_(context) {
|
||||
}
|
||||
|
||||
LLVMCodeUnitBuilder::~LLVMCodeUnitBuilder() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
Module* LLVMCodeUnitBuilder::module() {
|
||||
return module_;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::Init(const char* module_name,
|
||||
const char* module_path) {
|
||||
Reset();
|
||||
|
||||
// Create LLVM module.
|
||||
module_ = new Module(module_name, *context_);
|
||||
|
||||
// Create the emitter used to generate functions.
|
||||
emitter_ = new EmitterContext(memory_, context_, module_);
|
||||
|
||||
// Setup optimization pass.
|
||||
fpm_ = new FunctionPassManager(module_);
|
||||
if (FLAGS_optimize_ir_functions) {
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(*fpm_);
|
||||
}
|
||||
// TODO(benvanik): disable verifier in release builds?
|
||||
fpm_->add(createVerifierPass());
|
||||
|
||||
// Setup a debug info builder.
|
||||
// This is used when creating any debug info. We may want to go more
|
||||
// fine grained than this, but for now it's something.
|
||||
char dir[XE_MAX_PATH];
|
||||
XEIGNORE(xestrcpya(dir, XECOUNT(dir), module_path));
|
||||
char* slash = xestrrchra(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
di_builder_ = new DIBuilder(*module_);
|
||||
di_builder_->createCompileUnit(
|
||||
dwarf::DW_LANG_C99, //0x8010,
|
||||
StringRef(module_name),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeFunction(FunctionSymbol* symbol) {
|
||||
return MakeFunction(symbol, NULL);
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeFunction(FunctionSymbol* symbol,
|
||||
Function** out_fn) {
|
||||
int result_code = 0;
|
||||
|
||||
// Create the function (and setup args/attributes/etc).
|
||||
Function* fn = emitter_->GetFunction(symbol);
|
||||
|
||||
// If already defined, ignore.
|
||||
if (!fn->isDeclaration()) {
|
||||
if (out_fn) {
|
||||
*out_fn = fn;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
switch (symbol->type) {
|
||||
case FunctionSymbol::User:
|
||||
result_code = MakeUserFunction(symbol, fn);
|
||||
break;
|
||||
case FunctionSymbol::Kernel:
|
||||
if (symbol->kernel_export && symbol->kernel_export->is_implemented) {
|
||||
result_code = MakePresentImportFunction(symbol, fn);
|
||||
} else {
|
||||
result_code = MakeMissingImportFunction(symbol, fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
XEASSERTALWAYS();
|
||||
return 1;
|
||||
}
|
||||
if (result_code) {
|
||||
return result_code;
|
||||
}
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
OptimizeFunction(fn);
|
||||
|
||||
// Add to map for processing later.
|
||||
functions_.insert(std::pair<uint32_t, Function*>(symbol->start_address, fn));
|
||||
|
||||
if (out_fn) {
|
||||
*out_fn = fn;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeUserFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
// Setup emitter.
|
||||
emitter_->Init(symbol, fn);
|
||||
|
||||
// Emit.
|
||||
emitter_->GenerateBasicBlocks();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakePresentImportFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
LLVMContext& context = *context_;
|
||||
|
||||
// Pick names.
|
||||
// We have both the shim function pointer and the shim data pointer.
|
||||
char shim_name[256];
|
||||
xesnprintfa(shim_name, XECOUNT(shim_name),
|
||||
"__shim_%s", symbol->kernel_export->name);
|
||||
char shim_data_name[256];
|
||||
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
|
||||
"__shim_data_%s", symbol->kernel_export->name);
|
||||
|
||||
// Hardcoded to 64bits.
|
||||
Type* intPtrTy = IntegerType::get(context, 64);
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
// Declare shim function.
|
||||
std::vector<Type*> shimArgs;
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
shimArgs.push_back(int8PtrTy);
|
||||
FunctionType* shimTy = FunctionType::get(
|
||||
Type::getVoidTy(context), shimArgs, false);
|
||||
Function* shim = Function::Create(
|
||||
shimTy, Function::ExternalLinkage, shim_name, module_);
|
||||
|
||||
GlobalVariable* shim_data = new GlobalVariable(
|
||||
*module_, int8PtrTy, false, GlobalValue::ExternalLinkage, 0,
|
||||
shim_data_name);
|
||||
shim_data->setInitializer(ConstantExpr::getIntToPtr(
|
||||
ConstantInt::get(intPtrTy, 0), int8PtrTy));
|
||||
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", fn);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = module_->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
fn->arg_begin(),
|
||||
b.getInt64(symbol->start_address),
|
||||
++fn->arg_begin(),
|
||||
b.getInt64((uint64_t)symbol->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateCall2(
|
||||
shim,
|
||||
fn->arg_begin(),
|
||||
b.CreateLoad(shim_data));
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::MakeMissingImportFunction(FunctionSymbol* symbol,
|
||||
Function* fn) {
|
||||
BasicBlock* block = BasicBlock::Create(*context_, "entry", fn);
|
||||
IRBuilder<> b(block);
|
||||
|
||||
if (FLAGS_trace_kernel_calls) {
|
||||
Value* traceKernelCall = module_->getFunction("XeTraceKernelCall");
|
||||
b.CreateCall4(
|
||||
traceKernelCall,
|
||||
fn->arg_begin(),
|
||||
b.getInt64(symbol->start_address),
|
||||
++fn->arg_begin(),
|
||||
b.getInt64((uint64_t)symbol->kernel_export));
|
||||
}
|
||||
|
||||
b.CreateRetVoid();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LLVMCodeUnitBuilder::OptimizeFunction(Function* fn) {
|
||||
//fn->dump();
|
||||
fpm_->run(*fn);
|
||||
fn->dump();
|
||||
}
|
||||
|
||||
int LLVMCodeUnitBuilder::Finalize() {
|
||||
// Finalize debug info.
|
||||
di_builder_->finalize();
|
||||
|
||||
// TODO(benvanik): run module optimizations, if enabled.
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void LLVMCodeUnitBuilder::Reset() {
|
||||
functions_.clear();
|
||||
|
||||
delete emitter_;
|
||||
emitter_ = NULL;
|
||||
delete di_builder_;
|
||||
di_builder_ = NULL;
|
||||
delete fpm_;
|
||||
fpm_ = NULL;
|
||||
delete module_;
|
||||
module_ = NULL;
|
||||
}
|
|
@ -1,77 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/code_unit_builder.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class DIBuilder;
|
||||
class Function;
|
||||
class FunctionPassManager;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
class MDNode;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class EmitterContext;
|
||||
|
||||
|
||||
class LLVMCodeUnitBuilder : public CodeUnitBuilder {
|
||||
public:
|
||||
LLVMCodeUnitBuilder(xe_memory_ref memory, llvm::LLVMContext* context);
|
||||
virtual ~LLVMCodeUnitBuilder();
|
||||
|
||||
llvm::Module* module();
|
||||
|
||||
virtual int Init(const char* module_name, const char* module_path);
|
||||
virtual int MakeFunction(sdb::FunctionSymbol* symbol);
|
||||
int MakeFunction(sdb::FunctionSymbol* symbol, llvm::Function** out_fn);
|
||||
virtual int Finalize();
|
||||
virtual void Reset();
|
||||
|
||||
private:
|
||||
llvm::Function* CreateFunctionDeclaration(const char* name);
|
||||
int MakeUserFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
int MakePresentImportFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
int MakeMissingImportFunction(sdb::FunctionSymbol* symbol,
|
||||
llvm::Function* f);
|
||||
void OptimizeFunction(llvm::Function* f);
|
||||
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::Module* module_;
|
||||
llvm::FunctionPassManager* fpm_;
|
||||
llvm::DIBuilder* di_builder_;
|
||||
llvm::MDNode* cu_;
|
||||
EmitterContext* emitter_;
|
||||
|
||||
std::map<uint32_t, llvm::Function*> functions_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_CODE_UNIT_BUILDER_H_
|
|
@ -1,96 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_exports.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
|
||||
|
||||
void xe::cpu::llvmbe::SetupLlvmExports(
|
||||
GlobalExports* global_exports,
|
||||
Module* module, const DataLayout* dl, ExecutionEngine* engine) {
|
||||
LLVMContext& context = module->getContext();
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
|
||||
// Control methods:
|
||||
std::vector<Type*> trapArgs;
|
||||
trapArgs.push_back(int8PtrTy);
|
||||
trapArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* trapTy = FunctionType::get(
|
||||
Type::getVoidTy(context), trapArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
trapTy, Function::ExternalLinkage, "XeTrap",
|
||||
module), (void*)(global_exports->XeTrap));
|
||||
|
||||
std::vector<Type*> indirectBranchArgs;
|
||||
indirectBranchArgs.push_back(int8PtrTy);
|
||||
indirectBranchArgs.push_back(Type::getInt64Ty(context));
|
||||
indirectBranchArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* indirectBranchTy = FunctionType::get(
|
||||
Type::getVoidTy(context), indirectBranchArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
indirectBranchTy, Function::ExternalLinkage, "XeIndirectBranch",
|
||||
module), (void*)(global_exports->XeIndirectBranch));
|
||||
|
||||
// Debugging methods:
|
||||
std::vector<Type*> invalidInstructionArgs;
|
||||
invalidInstructionArgs.push_back(int8PtrTy);
|
||||
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
invalidInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* invalidInstructionTy = FunctionType::get(
|
||||
Type::getVoidTy(context), invalidInstructionArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
invalidInstructionTy, Function::ExternalLinkage, "XeInvalidInstruction",
|
||||
module), (void*)(global_exports->XeInvalidInstruction));
|
||||
|
||||
std::vector<Type*> accessViolationArgs;
|
||||
accessViolationArgs.push_back(int8PtrTy);
|
||||
accessViolationArgs.push_back(Type::getInt32Ty(context));
|
||||
accessViolationArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* accessViolationTy = FunctionType::get(
|
||||
Type::getVoidTy(context), accessViolationArgs, false);
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
accessViolationTy, Function::ExternalLinkage, "XeAccessViolation",
|
||||
module), (void*)(global_exports->XeAccessViolation));
|
||||
|
||||
// Tracing methods:
|
||||
std::vector<Type*> traceCallArgs;
|
||||
traceCallArgs.push_back(int8PtrTy);
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
traceCallArgs.push_back(Type::getInt64Ty(context));
|
||||
FunctionType* traceCallTy = FunctionType::get(
|
||||
Type::getVoidTy(context), traceCallArgs, false);
|
||||
std::vector<Type*> traceInstructionArgs;
|
||||
traceInstructionArgs.push_back(int8PtrTy);
|
||||
traceInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
traceInstructionArgs.push_back(Type::getInt32Ty(context));
|
||||
FunctionType* traceInstructionTy = FunctionType::get(
|
||||
Type::getVoidTy(context), traceInstructionArgs, false);
|
||||
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceKernelCall",
|
||||
module), (void*)(global_exports->XeTraceKernelCall));
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceCallTy, Function::ExternalLinkage, "XeTraceUserCall",
|
||||
module), (void*)(global_exports->XeTraceUserCall));
|
||||
engine->addGlobalMapping(Function::Create(
|
||||
traceInstructionTy, Function::ExternalLinkage, "XeTraceInstruction",
|
||||
module), (void*)(global_exports->XeTraceInstruction));
|
||||
}
|
|
@ -1,42 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_EXPORTS_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/global_exports.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class Module;
|
||||
class DataLayout;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
void SetupLlvmExports(
|
||||
GlobalExports* global_exports,
|
||||
llvm::Module* module, const llvm::DataLayout* dl,
|
||||
llvm::ExecutionEngine* engine);
|
||||
|
||||
|
||||
} // llvmbe
|
||||
} // cpu
|
||||
} // xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_EXPORTS_H_
|
|
@ -1,211 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_jit.h>
|
||||
|
||||
#include <llvm/ExecutionEngine/ExecutionEngine.h>
|
||||
#include <llvm/ExecutionEngine/GenericValue.h>
|
||||
#include <llvm/ExecutionEngine/JIT.h>
|
||||
#include <llvm/IR/Constants.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
|
||||
#include <xenia/cpu/exec_module.h>
|
||||
#include <xenia/cpu/llvmbe/llvm_code_unit_builder.h>
|
||||
#include <xenia/cpu/llvmbe/llvm_exports.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
LLVMJIT::LLVMJIT(xe_memory_ref memory, FunctionTable* fn_table) :
|
||||
JIT(memory, fn_table),
|
||||
context_(NULL), engine_(NULL), module_(NULL), cub_(NULL) {
|
||||
}
|
||||
|
||||
LLVMJIT::~LLVMJIT() {
|
||||
delete cub_;
|
||||
if (engine_) {
|
||||
engine_->removeModule(module_);
|
||||
}
|
||||
delete engine_;
|
||||
delete context_;
|
||||
}
|
||||
|
||||
//namespace {
|
||||
//void* LazyFunctionCreator(const std::string& name) {
|
||||
// printf("lazy: %s", name.c_str());
|
||||
// return NULL;
|
||||
//}
|
||||
//}
|
||||
|
||||
int LLVMJIT::Setup() {
|
||||
int result_code = 1;
|
||||
std::string error_message;
|
||||
|
||||
context_ = new LLVMContext();
|
||||
|
||||
// Create a shared code unit builder used while JITing.
|
||||
// Since there's only one we'll need to lock when generating code.
|
||||
// In the future we could try to generate new code in separate CUBs and then
|
||||
// link into the main module under the lock.
|
||||
cub_ = new LLVMCodeUnitBuilder(memory_, context_);
|
||||
result_code = cub_->Init("jit", "");
|
||||
if (result_code) {
|
||||
return result_code;
|
||||
}
|
||||
module_ = cub_->module();
|
||||
|
||||
EngineBuilder builder(module_);
|
||||
builder.setEngineKind(EngineKind::JIT);
|
||||
builder.setErrorStr(&error_message);
|
||||
builder.setOptLevel(CodeGenOpt::None);
|
||||
//builder.setOptLevel(CodeGenOpt::Aggressive);
|
||||
//builder.setJITMemoryManager(jmm);
|
||||
//builder.setTargetOptions();
|
||||
builder.setAllocateGVsWithCode(false);
|
||||
//builder.setUseMCJIT(true);
|
||||
|
||||
engine_ = builder.create();
|
||||
XEEXPECTNOTNULL(engine_);
|
||||
//engine_->DisableSymbolSearching();
|
||||
//engine_->InstallLazyFunctionCreator(LazyFunctionCreator);
|
||||
|
||||
XEEXPECTZERO(InjectGlobals());
|
||||
|
||||
result_code = 0;
|
||||
XECLEANUP:
|
||||
return result_code;
|
||||
}
|
||||
|
||||
int LLVMJIT::InjectGlobals() {
|
||||
LLVMContext& context = *context_;
|
||||
const DataLayout* dl = engine_->getDataLayout();
|
||||
Type* intPtrTy = dl->getIntPtrType(context);
|
||||
Type* int8PtrTy = PointerType::getUnqual(Type::getInt8Ty(context));
|
||||
GlobalVariable* gv;
|
||||
|
||||
// xe_memory_base
|
||||
// This is the base void* pointer to the memory space.
|
||||
gv = new GlobalVariable(
|
||||
*module_,
|
||||
int8PtrTy,
|
||||
true,
|
||||
GlobalValue::ExternalLinkage,
|
||||
0,
|
||||
"xe_memory_base");
|
||||
// Align to 64b - this makes SSE faster.
|
||||
gv->setAlignment(64);
|
||||
gv->setInitializer(ConstantExpr::getIntToPtr(
|
||||
ConstantInt::get(intPtrTy, (uintptr_t)xe_memory_addr(memory_, 0)),
|
||||
int8PtrTy));
|
||||
|
||||
// Setup global exports (the Xe* functions called by generated code).
|
||||
GlobalExports global_exports;
|
||||
cpu::GetGlobalExports(&global_exports);
|
||||
SetupLlvmExports(&global_exports, module_, dl, engine_);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMJIT::InitModule(ExecModule* module) {
|
||||
SymbolDatabase* sdb = module->sdb();
|
||||
Module* cub_module = cub_->module();
|
||||
|
||||
// Setup all imports (as needed).
|
||||
std::vector<FunctionSymbol*> functions;
|
||||
if (sdb->GetAllFunctions(functions)) {
|
||||
return 1;
|
||||
}
|
||||
int result_code = 0;
|
||||
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
|
||||
it != functions.end(); ++it) {
|
||||
FunctionSymbol* symbol = *it;
|
||||
if (symbol->type == FunctionSymbol::Kernel) {
|
||||
// Generate the function.
|
||||
Function* fn = NULL;
|
||||
result_code = cub_->MakeFunction(symbol, &fn);
|
||||
if (result_code) {
|
||||
XELOGE("Unable to generate import %s", symbol->name());
|
||||
return result_code;
|
||||
}
|
||||
|
||||
// Set global mappings for shim and data.
|
||||
char shim_name[256];
|
||||
xesnprintfa(shim_name, XECOUNT(shim_name),
|
||||
"__shim_%s", symbol->kernel_export->name);
|
||||
Function* shim = cub_module->getFunction(shim_name);
|
||||
if (shim) {
|
||||
engine_->updateGlobalMapping(
|
||||
shim, (void*)symbol->kernel_export->function_data.shim);
|
||||
}
|
||||
char shim_data_name[256];
|
||||
xesnprintfa(shim_data_name, XECOUNT(shim_data_name),
|
||||
"__shim_data_%s", symbol->kernel_export->name);
|
||||
GlobalVariable* shim_data = cub_module->getGlobalVariable(shim_data_name);
|
||||
if (shim_data) {
|
||||
engine_->updateGlobalMapping(
|
||||
shim_data, (void*)symbol->kernel_export->function_data.shim_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int LLVMJIT::UninitModule(ExecModule* module) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
FunctionPointer LLVMJIT::GenerateFunction(FunctionSymbol* symbol) {
|
||||
// In theory this is only called when required, so just assume we need to
|
||||
// generate it.
|
||||
|
||||
// Lock the function for adding.
|
||||
// If this fails it means the function exists and we don't need to generate
|
||||
// it.
|
||||
FunctionPointer ptr = fn_table_->BeginAddFunction(symbol->start_address);
|
||||
if (ptr) {
|
||||
return ptr;
|
||||
}
|
||||
|
||||
printf("generating %s...\n", symbol->name());
|
||||
|
||||
Function* fn = NULL;
|
||||
int result_code = cub_->MakeFunction(symbol, &fn);
|
||||
if (result_code) {
|
||||
XELOGE("Unable to generate function %s", symbol->name());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// LLVM requires all functions called to be defined at the time of
|
||||
// getPointerToFunction, so this does a recursive depth-first walk
|
||||
// to generate them.
|
||||
for (std::vector<FunctionCall>::iterator it = symbol->outgoing_calls.begin();
|
||||
it != symbol->outgoing_calls.end(); ++it) {
|
||||
FunctionSymbol* target = it->target;
|
||||
printf("needs dep fn %s\n", target->name());
|
||||
GenerateFunction(target);
|
||||
}
|
||||
|
||||
// Generate the machine code.
|
||||
ptr = (FunctionPointer)engine_->getPointerToFunction(fn);
|
||||
|
||||
// Add to the function table.
|
||||
fn_table_->AddFunction(symbol->start_address, ptr);
|
||||
|
||||
return ptr;
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_JIT_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_JIT_H_
|
||||
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/function_table.h>
|
||||
#include <xenia/cpu/jit.h>
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/sdb.h>
|
||||
|
||||
|
||||
namespace llvm {
|
||||
class ExecutionEngine;
|
||||
class LLVMContext;
|
||||
class Module;
|
||||
}
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMCodeUnitBuilder;
|
||||
|
||||
|
||||
class LLVMJIT : public JIT {
|
||||
public:
|
||||
LLVMJIT(xe_memory_ref memory, FunctionTable* fn_table);
|
||||
virtual ~LLVMJIT();
|
||||
|
||||
virtual int Setup();
|
||||
|
||||
virtual int InitModule(ExecModule* module);
|
||||
virtual int UninitModule(ExecModule* module);
|
||||
|
||||
virtual FunctionPointer GenerateFunction(sdb::FunctionSymbol* symbol);
|
||||
|
||||
protected:
|
||||
int InjectGlobals();
|
||||
|
||||
llvm::LLVMContext* context_;
|
||||
llvm::ExecutionEngine* engine_;
|
||||
llvm::Module* module_;
|
||||
|
||||
LLVMCodeUnitBuilder* cub_;
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_JIT_H_
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_library_linker.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
LLVMLibraryLinker::LLVMLibraryLinker(
|
||||
xe_memory_ref memory, kernel::ExportResolver* export_resolver) :
|
||||
LibraryLinker(memory, export_resolver) {
|
||||
}
|
||||
|
||||
LLVMLibraryLinker::~LLVMLibraryLinker() {
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/library_linker.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMLibraryLinker : public LibraryLinker {
|
||||
public:
|
||||
LLVMLibraryLinker(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver);
|
||||
virtual ~LLVMLibraryLinker();
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_LIBRARY_LINKER_H_
|
|
@ -1,24 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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 <xenia/cpu/llvmbe/llvm_library_loader.h>
|
||||
|
||||
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::llvmbe;
|
||||
|
||||
|
||||
LLVMLibraryLoader::LLVMLibraryLoader(
|
||||
xe_memory_ref memory, kernel::ExportResolver* export_resolver) :
|
||||
LibraryLoader(memory, export_resolver) {
|
||||
}
|
||||
|
||||
LLVMLibraryLoader::~LLVMLibraryLoader() {
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* 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_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
||||
#define XENIA_CPU_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
||||
|
||||
#include <xenia/common.h>
|
||||
#include <xenia/core.h>
|
||||
|
||||
#include <xenia/cpu/library_loader.h>
|
||||
|
||||
|
||||
namespace xe {
|
||||
namespace cpu {
|
||||
namespace llvmbe {
|
||||
|
||||
|
||||
class LLVMLibraryLoader : public LibraryLoader {
|
||||
public:
|
||||
LLVMLibraryLoader(xe_memory_ref memory,
|
||||
kernel::ExportResolver* export_resolver);
|
||||
virtual ~LLVMLibraryLoader();
|
||||
};
|
||||
|
||||
|
||||
} // namespace llvmbe
|
||||
} // namespace cpu
|
||||
} // namespace xe
|
||||
|
||||
|
||||
#endif // XENIA_CPU_LLVMBE_LLVM_LIBRARY_LOADER_H_
|
|
@ -1,24 +0,0 @@
|
|||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'emit.h',
|
||||
'emit_alu.cc',
|
||||
'emit_control.cc',
|
||||
'emit_fpu.cc',
|
||||
'emit_memory.cc',
|
||||
'emitter_context.cc',
|
||||
'emitter_context.h',
|
||||
'llvm_backend.cc',
|
||||
'llvm_backend.h',
|
||||
'llvm_code_unit_builder.cc',
|
||||
'llvm_code_unit_builder.h',
|
||||
'llvm_exports.cc',
|
||||
'llvm_exports.h',
|
||||
'llvm_jit.cc',
|
||||
'llvm_jit.h',
|
||||
'llvm_library_linker.cc',
|
||||
'llvm_library_linker.h',
|
||||
'llvm_library_loader.cc',
|
||||
'llvm_library_loader.h',
|
||||
],
|
||||
}
|
Loading…
Reference in New Issue