diff --git a/src/xenia/cpu/libjit/libjit_emit_control.cc b/src/xenia/cpu/libjit/libjit_emit_control.cc index 8cd1f12e9..8e6924a00 100644 --- a/src/xenia/cpu/libjit/libjit_emit_control.cc +++ b/src/xenia/cpu/libjit/libjit_emit_control.cc @@ -20,687 +20,692 @@ namespace cpu { namespace libjit { -// int XeEmitIndirectBranchTo( -// LibjitEmitter& e, jit_function_t f, 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. - -// jit_value_t 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.fn(), next_block); -// jit_value_t lr_cmp = b.CreateICmpEQ(target, ++(e.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( -// LibjitEmitter& e, jit_function_t f, 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.fn()->arg_begin(); -// jit_value_t 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, e.get_int64(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 )(LibjitEmitter& e, jit_function_t f, 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 )(LibjitEmitter& e, jit_function_t f, 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)); -// } - -// jit_value_t ctr_ok = NULL; -// if (XESELECTBITS(i.B.BO, 2, 2)) { -// // Ignore ctr. -// } else { -// // Decrement counter. -// jit_value_t ctr = e.ctr_value(); -// ctr = b.CreateSub(ctr, e.get_int64(1)); -// e.update_ctr_value(ctr); - -// // Ctr check. -// if (XESELECTBITS(i.B.BO, 1, 1)) { -// ctr_ok = b.CreateICmpEQ(ctr, e.get_int64(0)); -// } else { -// ctr_ok = b.CreateICmpNE(ctr, e.get_int64(0)); -// } -// } - -// jit_value_t cond_ok = NULL; -// if (XESELECTBITS(i.B.BO, 4, 4)) { -// // Ignore cond. -// } else { -// jit_value_t 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, e.get_int64(0)); -// } else { -// cond_ok = b.CreateICmpEQ(cr, e.get_int64(0)); -// } -// } - -// // We do a bit of optimization here to make the llvm assembly easier to read. -// jit_value_t 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.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 )(LibjitEmitter& e, jit_function_t f, 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)); -// } - -// jit_value_t cond_ok = NULL; -// if (XESELECTBITS(i.XL.BO, 4, 4)) { -// // Ignore cond. -// } else { -// jit_value_t 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, e.get_int64(0)); -// } else { -// cond_ok = b.CreateICmpEQ(cr, e.get_int64(0)); -// } -// } - -// // We do a bit of optimization here to make the llvm assembly easier to read. -// jit_value_t 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.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 )(LibjitEmitter& e, jit_function_t f, 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)); -// } - -// jit_value_t ctr_ok = NULL; -// if (XESELECTBITS(i.XL.BO, 2, 2)) { -// // Ignore ctr. -// } else { -// // Decrement counter. -// jit_value_t ctr = e.ctr_value(); -// ctr = b.CreateSub(ctr, e.get_int64(1)); - -// // Ctr check. -// if (XESELECTBITS(i.XL.BO, 1, 1)) { -// ctr_ok = b.CreateICmpEQ(ctr, e.get_int64(0)); -// } else { -// ctr_ok = b.CreateICmpNE(ctr, e.get_int64(0)); -// } -// } - -// jit_value_t cond_ok = NULL; -// if (XESELECTBITS(i.XL.BO, 4, 4)) { -// // Ignore cond. -// } else { -// jit_value_t 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, e.get_int64(0)); -// } else { -// cond_ok = b.CreateICmpEQ(cr, e.get_int64(0)); -// } -// } - -// // We do a bit of optimization here to make the llvm assembly easier to read. -// jit_value_t 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.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 )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(crandc, 0x4C000102, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(creqv, 0x4C000242, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(crnand, 0x4C0001C2, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(crnor, 0x4C000042, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(cror, 0x4C000382, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(crorc, 0x4C000342, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(crxor, 0x4C000182, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(mcrf, 0x4C000000, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - - -// // System linkage (A-24) - -// XEEMITTER(sc, 0x44000002, SC )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - - -// // Trap (A-25) - -// int XeEmitTrap(LibjitEmitter& e, jit_function_t f, InstrData& i, -// jit_value_t va, jit_value_t 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[4] then TRAP -// // Bits swapped: -// // 01234 -// // 43210 - -// if (!TO) { -// return 0; -// } - -// BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.fn(), -// e.GetNextBasicBlock()); -// BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.fn(), -// after_bb); - -// // Create the basic blocks (so we can chain). -// std::vector bbs; -// if (TO & (1 << 4)) { -// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); -// } -// if (TO & (1 << 3)) { -// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); -// } -// if (TO & (1 << 2)) { -// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); -// } -// if (TO & (1 << 1)) { -// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); -// } -// if (TO & (1 << 0)) { -// bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); -// } -// bbs.push_back(after_bb); - -// // Jump to the first bb. -// b.CreateBr(bbs.front()); - -// // Setup each basic block. -// std::vector::iterator it = bbs.begin(); -// if (TO & (1 << 4)) { -// // a < b -// BasicBlock* bb = *(it++); -// b.SetInsertPoint(bb); -// jit_value_t cmp = b.CreateICmpSLT(va, vb); -// b.CreateCondBr(cmp, trap_bb, *it); -// } -// if (TO & (1 << 3)) { -// // a > b -// BasicBlock* bb = *(it++); -// b.SetInsertPoint(bb); -// jit_value_t cmp = b.CreateICmpSGT(va, vb); -// b.CreateCondBr(cmp, trap_bb, *it); -// } -// if (TO & (1 << 2)) { -// // a = b -// BasicBlock* bb = *(it++); -// b.SetInsertPoint(bb); -// jit_value_t cmp = b.CreateICmpEQ(va, vb); -// b.CreateCondBr(cmp, trap_bb, *it); -// } -// if (TO & (1 << 1)) { -// // a u b -// BasicBlock* bb = *(it++); -// b.SetInsertPoint(bb); -// jit_value_t 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.fn()->arg_begin(), -// b.getInt32(i.address)); -// b.CreateBr(after_bb); - -// // Resume. -// b.SetInsertPoint(after_bb); - -// return 0; -// } - -// XEEMITTER(td, 0x7C000088, X )(LibjitEmitter& e, jit_function_t f, 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[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 )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP -// return XeEmitTrap(e, b, i, -// e.gpr_value(i.D.RA), -// e.get_int64(XEEXTS16(i.D.DS)), -// i.D.RT); -// } - -// XEEMITTER(tw, 0x7C000008, X )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP -// return XeEmitTrap(e, b, i, -// b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RA), -// b.getInt32Ty()), -// jit_type_nint), -// b.CreateSExt(b.CreateTrunc(e.gpr_value(i.X.RB), -// b.getInt32Ty()), -// jit_type_nint), -// i.X.RT); -// } - -// XEEMITTER(twi, 0x0C000000, D )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP -// return XeEmitTrap(e, b, i, -// b.CreateSExt(b.CreateTrunc(e.gpr_value(i.D.RA), -// b.getInt32Ty()), -// jit_type_nint), -// e.get_int64(XEEXTS16(i.D.DS)), -// i.D.RT); -// } - - -// // Processor control (A-26) - -// XEEMITTER(mfcr, 0x7C000026, X )(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(mfspr, 0x7C0002A6, XFX)(LibjitEmitter& e, jit_function_t f, 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); -// jit_value_t 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)(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(mtcrf, 0x7C000120, XFX)(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// XEINSTRNOTIMPLEMENTED(); -// return 1; -// } - -// XEEMITTER(mtspr, 0x7C0003A6, XFX)(LibjitEmitter& e, jit_function_t f, InstrData& i) { -// // n <- spr[5:9] || spr[0:4] -// // if length(SPR(n)) = 64 then -// // SPR(n) <- (RS) -// // else -// // SPR(n) <- (RS)[32:63] - -// jit_value_t 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; -// } +int XeEmitIndirectBranchTo( + LibjitEmitter& e, jit_function_t f, 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. + + jit_value_t 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) { + // The return block will spill registers for us. + // TODO(benvanik): 'lr_mismatch' debug info. + jit_value_t lr_cmp = jit_insn_eq(f, target, jit_value_get_param(f, 1)); + e.branch_to_return_if(lr_cmp); + } + + // 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( + LibjitEmitter& e, jit_function_t f, 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: + { + e.branch_to_block(fn_block->outgoing_address); + 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); + // TODO(benvanik): check to see if this is the last block in the function. + // This would enable tail calls/etc. + bool is_end = false; + if (!lk || is_end) { + // 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. + e.call_function(fn_block->outgoing_function, + jit_value_get_param(f, 1), true); + jit_insn_return(f, NULL); + } else { + // Will return here eventually. + // Refill registers from state. + e.call_function(fn_block->outgoing_function, + e.get_uint64(cia + 4), false); + e.FillRegisters(); + } + break; + } + case FunctionBlock::kTargetLR: + { + // An indirect jump. + printf("INDIRECT JUMP VIA LR: %.8X\n", cia); + return XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegLR); + } + case FunctionBlock::kTargetCTR: + { + // An indirect jump. + printf("INDIRECT JUMP VIA CTR: %.8X\n", cia); + return XeEmitIndirectBranchTo(e, f, src, cia, lk, kXEPPCRegCTR); + } + default: + case FunctionBlock::kTargetNone: + XEASSERTALWAYS(); + return 1; + } + return 0; +} + + +XEEMITTER(bx, 0x48000000, I )(LibjitEmitter& e, jit_function_t f, 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(e.get_uint64(i.address + 4)); + } + + return XeEmitBranchTo(e, f, "bx", i.address, i.I.LK); +} + +XEEMITTER(bcx, 0x40000000, B )(LibjitEmitter& e, jit_function_t f, 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(e.get_uint64(i.address + 4)); + } + + jit_value_t ctr_ok = NULL; + if (XESELECTBITS(i.B.BO, 2, 2)) { + // Ignore ctr. + } else { + // Decrement counter. + jit_value_t ctr = e.ctr_value(); + ctr = jit_insn_sub(f, ctr, e.get_int64(1)); + e.update_ctr_value(ctr); + + // Ctr check. + if (XESELECTBITS(i.B.BO, 1, 1)) { + ctr_ok = jit_insn_eq(f, ctr, e.get_int64(0)); + } else { + ctr_ok = jit_insn_ne(f, ctr, e.get_int64(0)); + } + } + + jit_value_t cond_ok = NULL; + if (XESELECTBITS(i.B.BO, 4, 4)) { + // Ignore cond. + } else { + jit_value_t cr = e.cr_value(i.B.BI >> 2); + cr = jit_insn_and(f, cr, e.get_uint32(1 << (i.B.BI & 3))); + if (XESELECTBITS(i.B.BO, 3, 3)) { + cond_ok = jit_insn_ne(f, cr, e.get_int64(0)); + } else { + cond_ok = jit_insn_eq(f, cr, e.get_int64(0)); + } + } + + // We do a bit of optimization here to make the llvm assembly easier to read. + jit_value_t ok = NULL; + if (ctr_ok && cond_ok) { + ok = jit_insn_and(f, ctr_ok, cond_ok); + } else if (ctr_ok) { + ok = ctr_ok; + } else if (cond_ok) { + ok = cond_ok; + } + + // Optimization: if a branch target, inline the conditional branch here. + FunctionBlock* fn_block = e.fn_block(); + if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) { + e.branch_to_block_if(fn_block->outgoing_address, ok); + } else { + // Only branch of conditionals when we have one. + jit_label_t post_jump_label = jit_label_undefined; + if (ok) { + // TODO(benvanik): add debug info for this? + // char name[32]; + // xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address); + jit_insn_branch_if_not(f, ok, &post_jump_label); + } + + // 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, f, "bcx", i.address, i.B.LK)) { + return 1; + } + + if (ok) { + jit_insn_label(f, &post_jump_label); + } + } + + return 0; +} + +XEEMITTER(bcctrx, 0x4C000420, XL )(LibjitEmitter& e, jit_function_t f, 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(e.get_uint64(i.address + 4)); + } + + jit_value_t cond_ok = NULL; + if (XESELECTBITS(i.XL.BO, 4, 4)) { + // Ignore cond. + } else { + jit_value_t cr = e.cr_value(i.XL.BI >> 2); + cr = jit_insn_and(f, cr, e.get_uint64(1 << (i.XL.BI & 3))); + if (XESELECTBITS(i.XL.BO, 3, 3)) { + cond_ok = jit_insn_ne(f, cr, e.get_int64(0)); + } else { + cond_ok = jit_insn_eq(f, cr, e.get_int64(0)); + } + } + + // We do a bit of optimization here to make the llvm assembly easier to read. + jit_value_t ok = NULL; + if (cond_ok) { + ok = cond_ok; + } + + // Optimization: if a branch target, inline the conditional branch here. + FunctionBlock* fn_block = e.fn_block(); + if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) { + e.branch_to_block_if(fn_block->outgoing_address, ok); + } else { + // Only branch of conditionals when we have one. + jit_label_t post_jump_label = jit_label_undefined; + if (ok) { + // TODO(benvanik): add debug info for this? + // char name[32]; + // xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address); + jit_insn_branch_if_not(f, ok, &post_jump_label); + } + + // Note that this occurs entirely within the branch true block. + if (XeEmitBranchTo(e, f, "bcctrx", i.address, i.XL.LK)) { + return 1; + } + + if (ok) { + jit_insn_label(f, &post_jump_label); + } + } + + return 0; +} + +XEEMITTER(bclrx, 0x4C000020, XL )(LibjitEmitter& e, jit_function_t f, 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(e.get_uint64(i.address + 4)); + } + + jit_value_t ctr_ok = NULL; + if (XESELECTBITS(i.XL.BO, 2, 2)) { + // Ignore ctr. + } else { + // Decrement counter. + jit_value_t ctr = e.ctr_value(); + ctr = jit_insn_sub(f, ctr, e.get_int64(1)); + + // Ctr check. + if (XESELECTBITS(i.XL.BO, 1, 1)) { + ctr_ok = jit_insn_eq(f, ctr, e.get_int64(0)); + } else { + ctr_ok = jit_insn_ne(f, ctr, e.get_int64(0)); + } + } + + jit_value_t cond_ok = NULL; + if (XESELECTBITS(i.XL.BO, 4, 4)) { + // Ignore cond. + } else { + jit_value_t cr = e.cr_value(i.XL.BI >> 2); + cr = jit_insn_and(f, cr, e.get_uint32(1 << (i.XL.BI & 3))); + if (XESELECTBITS(i.XL.BO, 3, 3)) { + cond_ok = jit_insn_ne(f, cr, e.get_int64(0)); + } else { + cond_ok = jit_insn_eq(f, cr, e.get_int64(0)); + } + } + + // We do a bit of optimization here to make the llvm assembly easier to read. + jit_value_t ok = NULL; + if (ctr_ok && cond_ok) { + ok = jit_insn_and(f, ctr_ok, cond_ok); + } else if (ctr_ok) { + ok = ctr_ok; + } else if (cond_ok) { + ok = cond_ok; + } + + // Optimization: if a branch target, inline the conditional branch here. + FunctionBlock* fn_block = e.fn_block(); + if (fn_block->outgoing_type == FunctionBlock::kTargetBlock) { + e.branch_to_block_if(fn_block->outgoing_address, ok); + } else { + // Only branch of conditionals when we have one. + jit_label_t post_jump_label = jit_label_undefined; + if (ok) { + // TODO(benvanik): add debug info for this? + // char name[32]; + // xesnprintfa(name, XECOUNT(name), "loc_%.8X_bcx", i.address); + jit_insn_branch_if_not(f, ok, &post_jump_label); + } + + // Note that this occurs entirely within the branch true block. + if (XeEmitBranchTo(e, f, "bclrx", i.address, i.XL.LK)) { + return 1; + } + + if (ok) { + jit_insn_label(f, &post_jump_label); + } + } + + return 0; +} + + +// Condition register logical (A-23) + +XEEMITTER(crand, 0x4C000202, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(crandc, 0x4C000102, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(creqv, 0x4C000242, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(crnand, 0x4C0001C2, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(crnor, 0x4C000042, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(cror, 0x4C000382, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(crorc, 0x4C000342, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(crxor, 0x4C000182, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(mcrf, 0x4C000000, XL )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + + +// System linkage (A-24) + +XEEMITTER(sc, 0x44000002, SC )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + + +// Trap (A-25) + +int XeEmitTrap(LibjitEmitter& e, jit_function_t f, InstrData& i, + jit_value_t va, jit_value_t 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[4] then TRAP + // Bits swapped: + // 01234 + // 43210 + + if (!TO) { + return 0; + } + + // TODO(benvanik): port from LLVM + XEASSERTALWAYS(); + + // BasicBlock* after_bb = BasicBlock::Create(*e.context(), "", e.fn(), + // e.GetNextBasicBlock()); + // BasicBlock* trap_bb = BasicBlock::Create(*e.context(), "", e.fn(), + // after_bb); + + // // Create the basic blocks (so we can chain). + // std::vector bbs; + // if (TO & (1 << 4)) { + // bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); + // } + // if (TO & (1 << 3)) { + // bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); + // } + // if (TO & (1 << 2)) { + // bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); + // } + // if (TO & (1 << 1)) { + // bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); + // } + // if (TO & (1 << 0)) { + // bbs.push_back(BasicBlock::Create(*e.context(), "", e.fn(), trap_bb)); + // } + // bbs.push_back(after_bb); + + // // Jump to the first bb. + // b.CreateBr(bbs.front()); + + // // Setup each basic block. + // std::vector::iterator it = bbs.begin(); + // if (TO & (1 << 4)) { + // // a < b + // BasicBlock* bb = *(it++); + // b.SetInsertPoint(bb); + // jit_value_t cmp = b.CreateICmpSLT(va, vb); + // b.CreateCondBr(cmp, trap_bb, *it); + // } + // if (TO & (1 << 3)) { + // // a > b + // BasicBlock* bb = *(it++); + // b.SetInsertPoint(bb); + // jit_value_t cmp = b.CreateICmpSGT(va, vb); + // b.CreateCondBr(cmp, trap_bb, *it); + // } + // if (TO & (1 << 2)) { + // // a = b + // BasicBlock* bb = *(it++); + // b.SetInsertPoint(bb); + // jit_value_t cmp = b.CreateICmpEQ(va, vb); + // b.CreateCondBr(cmp, trap_bb, *it); + // } + // if (TO & (1 << 1)) { + // // a u b + // BasicBlock* bb = *(it++); + // b.SetInsertPoint(bb); + // jit_value_t 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.fn()->arg_begin(), + // e.get_uint64(i.address)); + // b.CreateBr(after_bb); + + // // Resume. + // b.SetInsertPoint(after_bb); + + return 0; +} + +XEEMITTER(td, 0x7C000088, X )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP + return XeEmitTrap(e, f, i, + e.gpr_value(i.X.RA), + e.gpr_value(i.X.RB), + i.X.RT); +} + +XEEMITTER(tdi, 0x08000000, D )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP + return XeEmitTrap(e, f, i, + e.gpr_value(i.D.RA), + e.get_int64(XEEXTS16(i.D.DS)), + i.D.RT); +} + +XEEMITTER(tw, 0x7C000008, X )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP + return XeEmitTrap(e, f, i, + e.sign_extend(e.trunc_to_int(e.gpr_value(i.X.RA)), + jit_type_nint), + e.sign_extend(e.trunc_to_int(e.gpr_value(i.X.RB)), + jit_type_nint), + i.X.RT); +} + +XEEMITTER(twi, 0x0C000000, D )(LibjitEmitter& e, jit_function_t f, 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[4] then TRAP + return XeEmitTrap(e, f, i, + e.sign_extend(e.trunc_to_int(e.gpr_value(i.D.RA)), + jit_type_nint), + e.get_int64(XEEXTS16(i.D.DS)), + i.D.RT); +} + + +// Processor control (A-26) + +XEEMITTER(mfcr, 0x7C000026, X )(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(mfspr, 0x7C0002A6, XFX)(LibjitEmitter& e, jit_function_t f, 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); + jit_value_t 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)(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(mtcrf, 0x7C000120, XFX)(LibjitEmitter& e, jit_function_t f, InstrData& i) { + XEINSTRNOTIMPLEMENTED(); + return 1; +} + +XEEMITTER(mtspr, 0x7C0003A6, XFX)(LibjitEmitter& e, jit_function_t f, InstrData& i) { + // n <- spr[5:9] || spr[0:4] + // if length(SPR(n)) = 64 then + // SPR(n) <- (RS) + // else + // SPR(n) <- (RS)[32:63] + + jit_value_t 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 LibjitRegisterEmitCategoryControl() { - // 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); + 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); } diff --git a/src/xenia/cpu/libjit/libjit_emitter.cc b/src/xenia/cpu/libjit/libjit_emitter.cc index 34edf65c3..7b11b8b23 100644 --- a/src/xenia/cpu/libjit/libjit_emitter.cc +++ b/src/xenia/cpu/libjit/libjit_emitter.cc @@ -483,27 +483,27 @@ void LibjitEmitter::GenerateBasicBlock(FunctionBlock* block) { //jit_insn_mark_breakpoint(fn_, 1, ia); if (FLAGS_trace_instructions) { - // SpillRegisters(); - // jit_insn_call_native( - // fn_, - // "XeTraceInstruction", - // global_exports_.XeTraceInstruction, - // global_export_signature_3_, - // trace_args, XECOUNT(trace_args), - // 0); + SpillRegisters(); + jit_insn_call_native( + fn_, + "XeTraceInstruction", + global_exports_.XeTraceInstruction, + global_export_signature_3_, + trace_args, XECOUNT(trace_args), + 0); } if (!i.type) { XELOGCPU("Invalid instruction %.8X %.8X", ia, i.code); - // SpillRegisters(); - // jit_insn_call_native( - // fn_, - // "XeInvalidInstruction", - // global_exports_.XeInvalidInstruction, - // global_export_signature_3_, - // trace_args, XECOUNT(trace_args), - // 0); - continue; + SpillRegisters(); + jit_insn_call_native( + fn_, + "XeInvalidInstruction", + global_exports_.XeInvalidInstruction, + global_export_signature_3_, + trace_args, XECOUNT(trace_args), + 0); + continue; } if (FLAGS_log_codegen) { @@ -697,10 +697,15 @@ int LibjitEmitter::branch_to_block(uint32_t address) { int LibjitEmitter::branch_to_block_if(uint32_t address, jit_value_t value) { std::map::iterator it = bbs_.find(address); - return jit_insn_branch_if(fn_, value, &it->second); + if (value) { + return jit_insn_branch_if(fn_, value, &it->second); + } else { + return jit_insn_branch(fn_, &it->second); + } } int LibjitEmitter::branch_to_block_if_not(uint32_t address, jit_value_t value) { + XEASSERTNOTNULL(value); std::map::iterator it = bbs_.find(address); return jit_insn_branch_if_not(fn_, value, &it->second); } @@ -717,101 +722,115 @@ int LibjitEmitter::branch_to_return_if_not(jit_value_t value) { return jit_insn_branch_if_not(fn_, value, &return_block_); } -//BasicBlock* LibjitEmitter::GetNextBasicBlock() { -// std::map::iterator it = bbs_.find( -// fn_block_->start_address); -// ++it; -// if (it != bbs_.end()) { -// return it->second; -// } -// return NULL; -//} +int LibjitEmitter::call_function(FunctionSymbol* target_symbol, + jit_value_t lr, bool tail) { + PrepareFunction(target_symbol); + jit_function_t target_fn = (jit_function_t)target_symbol->impl_value; + XEASSERTNOTNULL(target_fn); + int flags = 0; + if (tail) { + flags |= JIT_CALL_TAIL; + } + jit_value_t args[] = {jit_value_get_param(fn_, 0), lr}; + jit_insn_call(fn_, target_symbol->name(), target_fn, fn_signature_, + args, XECOUNT(args), flags); + return 1; +} -//int LibjitEmitter::GenerateIndirectionBranch(uint32_t cia, jit_value_t target, -// bool lk, bool likely_local) { -// // This function is called by the control emitters when they know that an -// // indirect branch is required. -// // It first tries to see if the branch is to an address within the function -// // and, if so, uses a local switch table. If that fails because we don't know -// // the block the function is regenerated (ACK!). If the target is external -// // then an external call occurs. -// -// IRBuilder<>& b = *builder_; -// BasicBlock* next_block = GetNextBasicBlock(); -// -// PushInsertPoint(); -// -// // Request builds of the indirection blocks on demand. -// // We can't build here because we don't know what registers will be needed -// // yet, so we just create the blocks and let GenerateSharedBlocks handle it -// // after we are done with all user instructions. -// if (!external_indirection_block_) { -// // Setup locals in the entry block. -// b.SetInsertPoint(&fn_->getEntryBlock()); -// locals_.indirection_target = b.CreateAlloca( -// jit_type_nuint, 0, "indirection_target"); -// locals_.indirection_cia = b.CreateAlloca( -// jit_type_nuint, 0, "indirection_cia"); -// -// external_indirection_block_ = BasicBlock::Create( -// *context_, "external_indirection_block", fn_, return_block_); -// } -// if (likely_local && !internal_indirection_block_) { -// internal_indirection_block_ = BasicBlock::Create( -// *context_, "internal_indirection_block", fn_, return_block_); -// } -// -// PopInsertPoint(); -// -// // Check to see if the target address is within the function. -// // If it is jump to that basic block. If the basic block is not found it means -// // we have a jump inside the function that wasn't identified via static -// // analysis. These are bad as they require function regeneration. -// if (likely_local) { -// // Note that we only support LK=0, as we are using shared tables. -// XEASSERT(!lk); -// b.CreateStore(target, locals_.indirection_target); -// b.CreateStore(b.getInt64(cia), locals_.indirection_cia); -// jit_value_t symbol_ge_cmp = b.CreateICmpUGE(target, b.getInt64(symbol_->start_address)); -// jit_value_t symbol_l_cmp = b.CreateICmpULT(target, b.getInt64(symbol_->end_address)); -// jit_value_t symbol_target_cmp = jit_insn_and(fn_, symbol_ge_cmp, symbol_l_cmp); -// b.CreateCondBr(symbol_target_cmp, -// internal_indirection_block_, external_indirection_block_); -// return 0; -// } -// -// // If we are LK=0 jump to the shared indirection block. This prevents us -// // from needing to fill the registers again after the call and shares more -// // code. -// if (!lk) { -// b.CreateStore(target, locals_.indirection_target); -// b.CreateStore(b.getInt64(cia), locals_.indirection_cia); -// b.CreateBr(external_indirection_block_); -// } else { -// // Slowest path - spill, call the external function, and fill. -// // We should avoid this at all costs. -// -// // Spill registers. We could probably share this. -// SpillRegisters(); -// -// // TODO(benvanik): keep function pointer lookup local. -// jit_value_t indirect_branch = gen_module_->getFunction("XeIndirectBranch"); -// b.CreateCall3(indirect_branch, -// fn_->arg_begin(), -// target, -// b.getInt64(cia)); -// -// if (next_block) { -// // Only refill if not a tail call. -// FillRegisters(); -// b.CreateBr(next_block); -// } else { -// b.CreateRetVoid(); -// } -// } -// -// return 0; -//} +int LibjitEmitter::GenerateIndirectionBranch(uint32_t cia, jit_value_t target, + bool lk, bool likely_local) { + // This function is called by the control emitters when they know that an + // indirect branch is required. + // It first tries to see if the branch is to an address within the function + // and, if so, uses a local switch table. If that fails because we don't know + // the block the function is regenerated (ACK!). If the target is external + // then an external call occurs. + + // TODO(benvanik): port indirection. + //XEASSERTALWAYS(); + + // BasicBlock* next_block = GetNextBasicBlock(); + + // PushInsertPoint(); + + // // Request builds of the indirection blocks on demand. + // // We can't build here because we don't know what registers will be needed + // // yet, so we just create the blocks and let GenerateSharedBlocks handle it + // // after we are done with all user instructions. + // if (!external_indirection_block_) { + // // Setup locals in the entry block. + // b.SetInsertPoint(&fn_->getEntryBlock()); + // locals_.indirection_target = b.CreateAlloca( + // jit_type_nuint, 0, "indirection_target"); + // locals_.indirection_cia = b.CreateAlloca( + // jit_type_nuint, 0, "indirection_cia"); + + // external_indirection_block_ = BasicBlock::Create( + // *context_, "external_indirection_block", fn_, return_block_); + // } + // if (likely_local && !internal_indirection_block_) { + // internal_indirection_block_ = BasicBlock::Create( + // *context_, "internal_indirection_block", fn_, return_block_); + // } + + // PopInsertPoint(); + + // // Check to see if the target address is within the function. + // // If it is jump to that basic block. If the basic block is not found it means + // // we have a jump inside the function that wasn't identified via static + // // analysis. These are bad as they require function regeneration. + // if (likely_local) { + // // Note that we only support LK=0, as we are using shared tables. + // XEASSERT(!lk); + // b.CreateStore(target, locals_.indirection_target); + // b.CreateStore(b.getInt64(cia), locals_.indirection_cia); + // jit_value_t symbol_ge_cmp = b.CreateICmpUGE(target, b.getInt64(symbol_->start_address)); + // jit_value_t symbol_l_cmp = b.CreateICmpULT(target, b.getInt64(symbol_->end_address)); + // jit_value_t symbol_target_cmp = jit_insn_and(fn_, symbol_ge_cmp, symbol_l_cmp); + // b.CreateCondBr(symbol_target_cmp, + // internal_indirection_block_, external_indirection_block_); + // return 0; + // } + + // // If we are LK=0 jump to the shared indirection block. This prevents us + // // from needing to fill the registers again after the call and shares more + // // code. + // if (!lk) { + // b.CreateStore(target, locals_.indirection_target); + // b.CreateStore(b.getInt64(cia), locals_.indirection_cia); + // b.CreateBr(external_indirection_block_); + // } else { + // // Slowest path - spill, call the external function, and fill. + // // We should avoid this at all costs. + + // // Spill registers. We could probably share this. + // SpillRegisters(); + + // // Issue the full indirection branch. + // jit_value_t branch_args[] = { + // jit_value_get_param(fn_, 0), + // target, + // get_uint64(cia), + // }; + // jit_insn_call_native( + // fn_, + // "XeIndirectBranch", + // global_exports_.XeIndirectBranch, + // global_export_signature_3_, + // branch_args, XECOUNT(branch_args), + // 0); + + // if (next_block) { + // // Only refill if not a tail call. + // FillRegisters(); + // b.CreateBr(next_block); + // } else { + // jit_insn_return(fn_, NULL); + // } + // } + + return 0; +} jit_value_t LibjitEmitter::LoadStateValue(size_t offset, jit_type_t type, const char* name) { diff --git a/src/xenia/cpu/libjit/libjit_emitter.h b/src/xenia/cpu/libjit/libjit_emitter.h index 26fce1c9f..be94c273d 100644 --- a/src/xenia/cpu/libjit/libjit_emitter.h +++ b/src/xenia/cpu/libjit/libjit_emitter.h @@ -55,6 +55,8 @@ public: int branch_to_return(); int branch_to_return_if(jit_value_t value); int branch_to_return_if_not(jit_value_t value); + int call_function(sdb::FunctionSymbol* target_symbol, jit_value_t lr, + bool tail); int GenerateIndirectionBranch(uint32_t cia, jit_value_t target, bool lk, bool likely_local); diff --git a/third_party/libjit b/third_party/libjit index b73375e72..d18139e9e 160000 --- a/third_party/libjit +++ b/third_party/libjit @@ -1 +1 @@ -Subproject commit b73375e72ef395800b8290bd02a3a0104b52a92c +Subproject commit d18139e9ed65cacce6db7d855212c9586e1f0361