diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index d02f19556e..9e8a966c71 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -166,12 +166,13 @@ set(SRCS Src/ActionReplay.cpp Src/PowerPC/Interpreter/Interpreter_SystemRegisters.cpp Src/PowerPC/Interpreter/Interpreter_Tables.cpp Src/PowerPC/JitCommon/JitBase.cpp - Src/PowerPC/JitCommon/JitCache.cpp) + Src/PowerPC/JitCommon/JitCache.cpp + Src/PowerPC/JitILCommon/IR.cpp + ) if(NOT _M_GENERIC) set(SRCS ${SRCS} Src/x64MemTools.cpp - Src/PowerPC/Jit64IL/IR.cpp Src/PowerPC/Jit64IL/IR_X86.cpp Src/PowerPC/Jit64IL/JitILAsm.cpp Src/PowerPC/Jit64IL/JitIL_Branch.cpp @@ -217,7 +218,15 @@ if(_M_ARM) Src/PowerPC/JitArm32/JitArm_Paired.cpp Src/PowerPC/JitArm32/JitArm_LoadStorePaired.cpp Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp - Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp) + Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp + #JitArmIL + Src/PowerPC/JitArmIL/JitIL.cpp + Src/PowerPC/JitArmIL/JitILAsm.cpp + Src/PowerPC/JitArmIL/JitIL_Tables.cpp + Src/PowerPC/JitArmIL/JitIL_Branch.cpp + Src/PowerPC/JitArmIL/JitIL_SystemRegisters.cpp + Src/PowerPC/JitArmIL/IR_Arm.cpp + ) endif() set(LIBS bdisasm inputcommon videosoftware sfml-network) diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.cpp new file mode 100644 index 0000000000..3ee7950f42 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.cpp @@ -0,0 +1,576 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. +#include "../JitILCommon/IR.h" +#include "../PPCTables.h" +#include "../../CoreTiming.h" +#include "../../HW/Memmap.h" +#include "JitILAsm.h" +#include "JitIL.h" +#include "ArmEmitter.h" +#include "../../Core.h" + +using namespace IREmitter; +using namespace ArmGen; +static const unsigned int MAX_NUMBER_OF_REGS = 32; + +struct RegInfo { + JitArmIL *Jit; + IRBuilder* Build; + InstLoc FirstI; + std::vector IInfo; + std::vector lastUsed; + InstLoc regs[MAX_NUMBER_OF_REGS]; + InstLoc fregs[MAX_NUMBER_OF_REGS]; + unsigned numSpills; + unsigned numFSpills; + unsigned exitNumber; + + RegInfo(JitArmIL* j, InstLoc f, unsigned insts) : Jit(j), FirstI(f), IInfo(insts), lastUsed(insts) { + for (unsigned i = 0; i < MAX_NUMBER_OF_REGS; i++) { + regs[i] = 0; + fregs[i] = 0; + } + numSpills = 0; + numFSpills = 0; + exitNumber = 0; + } + + private: + RegInfo(RegInfo&); // DO NOT IMPLEMENT +}; + +static const ARMReg RegAllocOrder[] = {R0, R1, R2, R3, R4, R5, R6, R7, R8}; +static const int RegAllocSize = sizeof(RegAllocOrder) / sizeof(ARMReg); + +static unsigned SlotSet[1000]; + +static void regMarkUse(RegInfo& R, InstLoc I, InstLoc Op, unsigned OpNum) { + unsigned& info = R.IInfo[Op - R.FirstI]; + if (info == 0) R.IInfo[I - R.FirstI] |= 1 << (OpNum + 1); + if (info < 2) info++; + R.lastUsed[Op - R.FirstI] = max(R.lastUsed[Op - R.FirstI], I); +} +static void regClearInst(RegInfo& RI, InstLoc I) { + for (int i = 0; i < RegAllocSize; i++) + if (RI.regs[RegAllocOrder[i]] == I) + RI.regs[RegAllocOrder[i]] = 0; +} + +static void regNormalRegClear(RegInfo& RI, InstLoc I) { + if (RI.IInfo[I - RI.FirstI] & 4) + regClearInst(RI, getOp1(I)); + if (RI.IInfo[I - RI.FirstI] & 8) + regClearInst(RI, getOp2(I)); +} + +static unsigned regReadUse(RegInfo& R, InstLoc I) { + return R.IInfo[I - R.FirstI] & 3; +} + +static u32 regLocForSlot(RegInfo& RI, unsigned slot) { + return (u32)&SlotSet[slot - 1]; +} + +static unsigned regCreateSpill(RegInfo& RI, InstLoc I) { + unsigned newSpill = ++RI.numSpills; + RI.IInfo[I - RI.FirstI] |= newSpill << 16; + return newSpill; +} + +static unsigned regGetSpill(RegInfo& RI, InstLoc I) { + return RI.IInfo[I - RI.FirstI] >> 16; +} + +static void regSpill(RegInfo& RI, ARMReg reg) { + if (!RI.regs[reg]) return; + unsigned slot = regGetSpill(RI, RI.regs[reg]); + if (!slot) { + slot = regCreateSpill(RI, RI.regs[reg]); + RI.Jit->MOVI2R(R14, regLocForSlot(RI, slot)); + RI.Jit->STR(reg, R14, 0); + } + RI.regs[reg] = 0; +} + +static ARMReg regFindFreeReg(RegInfo& RI) { + for (int i = 0; i < RegAllocSize; i++) + if (RI.regs[RegAllocOrder[i]] == 0) + return RegAllocOrder[i]; + + int bestIndex = -1; + InstLoc bestEnd = 0; + for (int i = 0; i < RegAllocSize; ++i) { + const InstLoc start = RI.regs[RegAllocOrder[i]]; + const InstLoc end = RI.lastUsed[start - RI.FirstI]; + if (bestEnd < end) { + bestEnd = end; + bestIndex = i; + } + } + + ARMReg reg = RegAllocOrder[bestIndex]; + regSpill(RI, reg); + return reg; +} +static ARMReg regLocForInst(RegInfo& RI, InstLoc I) { + for (int i = 0; i < RegAllocSize; i++) + if (RI.regs[RegAllocOrder[i]] == I) + return RegAllocOrder[i]; + + if (regGetSpill(RI, I) == 0) + PanicAlert("Retrieving unknown spill slot?!"); + RI.Jit->MOVI2R(R14, regLocForSlot(RI, regGetSpill(RI, I))); + ARMReg reg = regFindFreeReg(RI); + RI.Jit->LDR(reg, R14, 0); + return reg; +} +static void regSpillCallerSaved(RegInfo& RI) { + regSpill(RI, R0); + regSpill(RI, R1); + regSpill(RI, R2); + regSpill(RI, R3); +} + +static ARMReg regEnsureInReg(RegInfo& RI, InstLoc I) { + return regLocForInst(RI, I); +} + +static void regWriteExit(RegInfo& RI, InstLoc dest) { + if (isImm(*dest)) { + RI.Jit->WriteExit(RI.Build->GetImmValue(dest), RI.exitNumber++); + } else { + RI.Jit->WriteExitDestInReg(regLocForInst(RI, dest)); + } +} +static void regStoreInstToPPCState(RegInfo& RI, unsigned width, InstLoc I, s32 offset) { + if (width != 32) { + PanicAlert("Not implemented!"); + return; + } + if (isImm(*I)) { + RI.Jit->MOVI2R(R12, RI.Build->GetImmValue(I)); + RI.Jit->STR(R12, R9, offset); + return; + } + ARMReg reg = regEnsureInReg(RI, I); + RI.Jit->STR(reg, R9, offset); +} + +// +// Mark and calculation routines for profiled load/store addresses +// Could be extended to unprofiled addresses. +static void regMarkMemAddress(RegInfo& RI, InstLoc I, InstLoc AI, unsigned OpNum) { + if (isImm(*AI)) { + unsigned addr = RI.Build->GetImmValue(AI); + if (Memory::IsRAMAddress(addr)) + return; + } + if (getOpcode(*AI) == Add && isImm(*getOp2(AI))) { + regMarkUse(RI, I, getOp1(AI), OpNum); + return; + } + regMarkUse(RI, I, AI, OpNum); +} +// Binary ops +void JitArmIL::BIN_XOR(ARMReg reg, Operand2 op2) +{ + EOR(reg, reg, op2); +} +void JitArmIL::BIN_AND(ARMReg reg, Operand2 op2) +{ + AND(reg, reg, op2); +} +void JitArmIL::BIN_ADD(ARMReg reg, Operand2 op2) +{ + ADD(reg, reg, op2); +} + +static void regEmitBinInst(RegInfo& RI, InstLoc I, + void (JitArmIL::*op)(ARMReg, Operand2), + bool commutable = false) { + ARMReg reg; + bool commuted = false; + if (RI.IInfo[I - RI.FirstI] & 4) { + reg = regEnsureInReg(RI, getOp1(I)); + } else if (commutable && (RI.IInfo[I - RI.FirstI] & 8)) { + reg = regEnsureInReg(RI, getOp2(I)); + commuted = true; + } else { + reg = regFindFreeReg(RI); + RI.Jit->MOV(reg, regLocForInst(RI, getOp1(I))); + } + if (isImm(*getOp2(I))) { + unsigned RHS = RI.Build->GetImmValue(getOp2(I)); + if (RHS + 128 < 256) { + (RI.Jit->*op)(reg, RHS); + } else { + (RI.Jit->*op)(reg, RHS); + } + } else if (commuted) { + (RI.Jit->*op)(reg, regLocForInst(RI, getOp1(I))); + } else { + (RI.Jit->*op)(reg, regLocForInst(RI, getOp2(I))); + } + RI.regs[reg] = I; + regNormalRegClear(RI, I); +} + +static void DoWriteCode(IRBuilder* ibuild, JitArmIL* Jit) { + RegInfo RI(Jit, ibuild->getFirstInst(), ibuild->getNumInsts()); + RI.Build = ibuild; + + // Pass to compute liveness + ibuild->StartBackPass(); + for (unsigned int index = (unsigned int)RI.IInfo.size() - 1; index != -1U; --index) { + InstLoc I = ibuild->ReadBackward(); + unsigned int op = getOpcode(*I); + bool thisUsed = regReadUse(RI, I) ? true : false; + switch (op) { + default: + PanicAlert("Unexpected inst!"); + case Nop: + case CInt16: + case CInt32: + case LoadGReg: + case LoadLink: + case LoadCR: + case LoadCarry: + case LoadCTR: + case LoadMSR: + case LoadFReg: + case LoadFRegDENToZero: + case LoadGQR: + case BlockEnd: + case BlockStart: + case InterpreterFallback: + case SystemCall: + case RFIExit: + case InterpreterBranch: + case ShortIdleLoop: + case FPExceptionCheck: + case DSIExceptionCheck: + case ISIException: + case ExtExceptionCheck: + case BreakPointCheck: + case Int3: + case Tramp: + // No liveness effects + break; + case SExt8: + case SExt16: + case BSwap32: + case BSwap16: + case Cntlzw: + case Not: + case DupSingleToMReg: + case DoubleToSingle: + case ExpandPackedToMReg: + case CompactMRegToPacked: + case FPNeg: + case FPDup0: + case FPDup1: + case FSNeg: + case FDNeg: + if (thisUsed) + regMarkUse(RI, I, getOp1(I), 1); + break; + case Load8: + case Load16: + case Load32: + regMarkMemAddress(RI, I, getOp1(I), 1); + break; + case LoadDouble: + case LoadSingle: + case LoadPaired: + if (thisUsed) + regMarkUse(RI, I, getOp1(I), 1); + break; + case StoreCR: + case StoreCarry: + case StoreFPRF: + regMarkUse(RI, I, getOp1(I), 1); + break; + case StoreGReg: + case StoreLink: + case StoreCTR: + case StoreMSR: + case StoreGQR: + case StoreSRR: + case StoreFReg: + if (!isImm(*getOp1(I))) + regMarkUse(RI, I, getOp1(I), 1); + break; + case Add: + case Sub: + case And: + case Or: + case Xor: + case Mul: + case MulHighUnsigned: + case Rol: + case Shl: + case Shrl: + case Sarl: + case ICmpCRUnsigned: + case ICmpCRSigned: + case ICmpEq: + case ICmpNe: + case ICmpUgt: + case ICmpUlt: + case ICmpUge: + case ICmpUle: + case ICmpSgt: + case ICmpSlt: + case ICmpSge: + case ICmpSle: + case FSMul: + case FSAdd: + case FSSub: + case FSRSqrt: + case FDMul: + case FDAdd: + case FDSub: + case FPAdd: + case FPMul: + case FPSub: + case FPMerge00: + case FPMerge01: + case FPMerge10: + case FPMerge11: + case FDCmpCR: + case InsertDoubleInMReg: + if (thisUsed) { + regMarkUse(RI, I, getOp1(I), 1); + if (!isImm(*getOp2(I))) + regMarkUse(RI, I, getOp2(I), 2); + } + break; + case Store8: + case Store16: + case Store32: + if (!isImm(*getOp1(I))) + regMarkUse(RI, I, getOp1(I), 1); + regMarkMemAddress(RI, I, getOp2(I), 2); + break; + case StoreSingle: + case StoreDouble: + case StorePaired: + regMarkUse(RI, I, getOp1(I), 1); + regMarkUse(RI, I, getOp2(I), 2); + break; + case BranchUncond: + if (!isImm(*getOp1(I))) + regMarkUse(RI, I, getOp1(I), 1); + break; + case IdleBranch: + regMarkUse(RI, I, getOp1(getOp1(I)), 1); + break; + case BranchCond: { + if (isICmp(*getOp1(I)) && + isImm(*getOp2(getOp1(I)))) { + regMarkUse(RI, I, getOp1(getOp1(I)), 1); + } else { + regMarkUse(RI, I, getOp1(I), 1); + } + if (!isImm(*getOp2(I))) + regMarkUse(RI, I, getOp2(I), 2); + break; + } + } + } + + ibuild->StartForwardPass(); + for (unsigned i = 0; i != RI.IInfo.size(); i++) { + InstLoc I = ibuild->ReadForward(); + + bool thisUsed = regReadUse(RI, I) ? true : false; + if (thisUsed) { + // Needed for IR Writer + ibuild->SetMarkUsed(I); + } + + switch (getOpcode(*I)) { + case CInt32: + case CInt16: { + if (!thisUsed) break; + ARMReg reg = regFindFreeReg(RI); + Jit->MOVI2R(reg, ibuild->GetImmValue(I)); + RI.regs[reg] = I; + break; + } + case BranchUncond: { + regWriteExit(RI, getOp1(I)); + regNormalRegClear(RI, I); + break; + } + case BranchCond: { + if (isICmp(*getOp1(I)) && + isImm(*getOp2(getOp1(I)))) { + Jit->MOVI2R(R14, RI.Build->GetImmValue(getOp2(getOp1(I)))); + Jit->CMP(regLocForInst(RI, getOp1(getOp1(I))), R14); + CCFlags flag; + switch (getOpcode(*getOp1(I))) { + case ICmpEq: flag = CC_NEQ; break; + case ICmpNe: flag = CC_EQ; break; + case ICmpUgt: flag = CC_LS; break; + case ICmpUlt: flag = CC_HI; break; + case ICmpUge: flag = CC_HS; break; + case ICmpUle: flag = CC_LO; break; + case ICmpSgt: flag = CC_LT; break; + case ICmpSlt: flag = CC_GT; break; + case ICmpSge: flag = CC_LE; break; + case ICmpSle: flag = CC_GE; break; + default: PanicAlert("cmpXX"); flag = CC_AL; break; + } + FixupBranch cont = Jit->B_CC(flag); + regWriteExit(RI, getOp2(I)); + Jit->SetJumpTarget(cont); + if (RI.IInfo[I - RI.FirstI] & 4) + regClearInst(RI, getOp1(getOp1(I))); + } else { + Jit->CMP(regLocForInst(RI, getOp1(I)), 0); + FixupBranch cont = Jit->B_CC(CC_EQ); + regWriteExit(RI, getOp2(I)); + Jit->SetJumpTarget(cont); + if (RI.IInfo[I - RI.FirstI] & 4) + regClearInst(RI, getOp1(I)); + } + if (RI.IInfo[I - RI.FirstI] & 8) + regClearInst(RI, getOp2(I)); + break; + } + + case StoreLink: { + regStoreInstToPPCState(RI, 32, getOp1(I), PPCSTATE_OFF(spr[SPR_LR])); + regNormalRegClear(RI, I); + break; + } + case StoreCTR: { + regStoreInstToPPCState(RI, 32, getOp1(I), PPCSTATE_OFF(spr[SPR_CTR])); + regNormalRegClear(RI, I); + break; + } + case StoreMSR: { + regStoreInstToPPCState(RI, 32, getOp1(I), PPCSTATE_OFF(msr)); + regNormalRegClear(RI, I); + break; + } + case LoadGReg: { + if (!thisUsed) break; + ARMReg reg = regFindFreeReg(RI); + unsigned ppcreg = *I >> 8; + Jit->LDR(reg, R9, PPCSTATE_OFF(gpr[ppcreg])); + RI.regs[reg] = I; + break; + } + case LoadCR: { + if (!thisUsed) break; + ARMReg reg = regFindFreeReg(RI); + unsigned ppcreg = *I >> 8; + Jit->LDRB(reg, R9, PPCSTATE_OFF(cr_fast[ppcreg])); + RI.regs[reg] = I; + break; + } + case LoadCTR: { + if (!thisUsed) break; + ARMReg reg = regFindFreeReg(RI); + Jit->LDR(reg, R9, PPCSTATE_OFF(spr[SPR_CTR])); + RI.regs[reg] = I; + break; + } + case LoadLink: { + if (!thisUsed) break; + ARMReg reg = regFindFreeReg(RI); + Jit->LDR(reg, R9, PPCSTATE_OFF(spr[SPR_LR])); + RI.regs[reg] = I; + break; + } + case InterpreterFallback: { + unsigned InstCode = ibuild->GetImmValue(getOp1(I)); + unsigned InstLoc = ibuild->GetImmValue(getOp2(I)); + // There really shouldn't be anything live across an + // interpreter call at the moment, but optimizing interpreter + // calls isn't completely out of the question... + regSpillCallerSaved(RI); + Jit->MOVI2R(R14, InstLoc); + Jit->STR(R14, R9, PPCSTATE_OFF(pc)); + Jit->MOVI2R(R14, InstLoc + 4); + Jit->STR(R14, R9, PPCSTATE_OFF(npc)); + + Jit->MOVI2R(R0, InstCode); + Jit->MOVI2R(R14, (u32)GetInterpreterOp(InstCode)); + Jit->BL(R14); + break; + } + case RFIExit: { + const u32 mask = 0x87C0FFFF; + const u32 clearMSR13 = 0xFFFBFFFF; // Mask used to clear the bit MSR[13] + // MSR = ((MSR & ~mask) | (SRR1 & mask)) & clearMSR13; + // R0 = MSR location + // R1 = MSR contents + // R2 = Mask + // R3 = Mask + ARMReg rA = R14; + ARMReg rB = R12; + ARMReg rC = R11; + ARMReg rD = R10; + Jit->MOVI2R(rB, (~mask) & clearMSR13); + Jit->MOVI2R(rC, mask & clearMSR13); + + Jit->LDR(rD, R9, PPCSTATE_OFF(msr)); + + Jit->AND(rD, rD, rB); // rD = Masked MSR + + Jit->LDR(rB, R9, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here + + Jit->AND(rB, rB, rC); // rB contains masked SRR1 here + Jit->ORR(rB, rD, rB); // rB = Masked MSR OR masked SRR1 + + Jit->STR(rB, R9, PPCSTATE_OFF(msr)); // STR rB in to rA + + Jit->LDR(rA, R9, PPCSTATE_OFF(spr[SPR_SRR0])); + + Jit->WriteRfiExitDestInR(rA); // rA gets unlocked here + break; + } + case And: { + if (!thisUsed) break; + regEmitBinInst(RI, I, &JitArmIL::BIN_AND, true); + break; + } + case Xor: { + if (!thisUsed) break; + regEmitBinInst(RI, I, &JitArmIL::BIN_XOR, true); + break; + } + case Add: { + if (!thisUsed) break; + regEmitBinInst(RI, I, &JitArmIL::BIN_ADD, true); + break; + } + default: + PanicAlert("Unknown JIT instruction; aborting!"); + ibuild->WriteToFile(0); + exit(1); + } + } + for (unsigned i = 0; i < MAX_NUMBER_OF_REGS; i++) { + if (RI.regs[i]) { + // Start a game in Burnout 2 to get this. Or animal crossing. + PanicAlert("Incomplete cleanup! (regs)"); + exit(1); + } + if (RI.fregs[i]) { + PanicAlert("Incomplete cleanup! (fregs)"); + exit(1); + } + } + + Jit->WriteExit(jit->js.curBlock->exitAddress[0], 0); + Jit->BKPT(0x111); + +} +void JitArmIL::WriteCode() { + DoWriteCode(&ibuild, this); +} diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.h b/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.h new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/IR_Arm.h @@ -0,0 +1 @@ + diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.cpp new file mode 100644 index 0000000000..e06c642730 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.cpp @@ -0,0 +1,334 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include + +#include "Common.h" +#include "../../HLE/HLE.h" +#include "../../Core.h" +#include "../../PatchEngine.h" +#include "../../CoreTiming.h" +#include "../../ConfigManager.h" +#include "../PowerPC.h" +#include "../Profiler.h" +#include "../PPCTables.h" +#include "../PPCAnalyst.h" +#include "../../HW/Memmap.h" +#include "../../HW/GPFifo.h" +#include "JitIL.h" +#include "JitIL_Tables.h" +#include "ArmEmitter.h" +#include "../JitInterface.h" + +using namespace ArmGen; +using namespace PowerPC; + +static int CODE_SIZE = 1024*1024*32; +namespace CPUCompare +{ + extern u32 m_BlockStart; +} +void JitArmIL::Init() +{ + AllocCodeSpace(CODE_SIZE); + blocks.Init(); + asm_routines.Init(); +} + +void JitArmIL::ClearCache() +{ + ClearCodeSpace(); + blocks.Clear(); +} + +void JitArmIL::Shutdown() +{ + FreeCodeSpace(); + blocks.Shutdown(); + asm_routines.Shutdown(); +} +void JitArmIL::unknown_instruction(UGeckoInstruction inst) +{ + // CCPU::Break(); + PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex); +} + +void JitArmIL::Default(UGeckoInstruction _inst) +{ + ibuild.EmitInterpreterFallback( + ibuild.EmitIntConst(_inst.hex), + ibuild.EmitIntConst(js.compilerPC)); +} + +void JitArmIL::HLEFunction(UGeckoInstruction _inst) +{ + // XXX +} + +void JitArmIL::DoNothing(UGeckoInstruction _inst) +{ + // Yup, just don't do anything. +} +void JitArmIL::WriteExitDestInReg(ARMReg Reg) +{ + STR(Reg, R9, PPCSTATE_OFF(pc)); + MOVI2R(Reg, (u32)asm_routines.dispatcher); + B(Reg); +} + +void JitArmIL::WriteRfiExitDestInR(ARMReg Reg) +{ + STR(Reg, R9, PPCSTATE_OFF(pc)); + + MOVI2R(Reg, (u32)asm_routines.testExceptions); + B(Reg); +} + +void JitArmIL::WriteExit(u32 destination, int exit_num) +{ + //If nobody has taken care of this yet (this can be removed when all branches are done) + JitBlock *b = js.curBlock; + b->exitAddress[exit_num] = destination; + b->exitPtrs[exit_num] = GetWritableCodePtr(); + + // Link opportunity! + int block = blocks.GetBlockNumberFromStartAddress(destination); + if (block >= 0 && jo.enableBlocklink) + { + // It exists! Joy of joy! + B(blocks.GetBlock(block)->checkedEntry); + b->linkStatus[exit_num] = true; + } + else + { + MOVI2R(R14, destination); + STR(R14, R9, PPCSTATE_OFF(pc)); + MOVI2R(R14, (u32)asm_routines.dispatcher); + B(R14); + } +} +void JitArmIL::PrintDebug(UGeckoInstruction inst, u32 level) +{ + if (level > 0) + printf("Start: %08x OP '%s' Info\n", (u32)GetCodePtr(), PPCTables::GetInstructionName(inst)); + if (level > 1) + { + GekkoOPInfo* Info = GetOpInfo(inst.hex); + printf("\tOuts\n"); + if (Info->flags & FL_OUT_A) + printf("\t-OUT_A: %x\n", inst.RA); + if(Info->flags & FL_OUT_D) + printf("\t-OUT_D: %x\n", inst.RD); + printf("\tIns\n"); + // A, AO, B, C, S + if(Info->flags & FL_IN_A) + printf("\t-IN_A: %x\n", inst.RA); + if(Info->flags & FL_IN_A0) + printf("\t-IN_A0: %x\n", inst.RA); + if(Info->flags & FL_IN_B) + printf("\t-IN_B: %x\n", inst.RB); + if(Info->flags & FL_IN_C) + printf("\t-IN_C: %x\n", inst.RC); + if(Info->flags & FL_IN_S) + printf("\t-IN_S: %x\n", inst.RS); + } +} + +void STACKALIGN JitArmIL::Run() +{ + CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode; + pExecAddr(); +} + +void JitArmIL::SingleStep() +{ + CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode; + pExecAddr(); +} +void STACKALIGN JitArmIL::Jit(u32 em_address) +{ + if (GetSpaceLeft() < 0x10000 || blocks.IsFull() || Core::g_CoreStartupParameter.bJITNoBlockCache) + { + ClearCache(); + } + + int block_num = blocks.AllocateBlock(PowerPC::ppcState.pc); + JitBlock *b = blocks.GetBlock(block_num); + const u8* BlockPtr = DoJit(PowerPC::ppcState.pc, &code_buffer, b); + blocks.FinalizeBlock(block_num, jo.enableBlocklink, BlockPtr); +} + +const u8* JitArmIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b) +{ + printf("Doing a JIT in JITARMIL. Scream and shout.\n"); + int blockSize = code_buf->GetSize(); + // Memory exception on instruction fetch + bool memory_exception = false; + + // A broken block is a block that does not end in a branch + bool broken_block = false; + + if (Core::g_CoreStartupParameter.bEnableDebugging) + { + // Comment out the following to disable breakpoints (speed-up) + blockSize = 1; + broken_block = true; + } + + if (em_address == 0) + { + Core::SetState(Core::CORE_PAUSE); + PanicAlert("ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR); + } + + if (Core::g_CoreStartupParameter.bMMU && (em_address & JIT_ICACHE_VMEM_BIT)) + { + if (!Memory::TranslateAddress(em_address, Memory::FLAG_OPCODE)) + { + // Memory exception occurred during instruction fetch + memory_exception = true; + } + } + + + int size = 0; + js.isLastInstruction = false; + js.blockStart = em_address; + js.fifoBytesThisBlock = 0; + js.curBlock = b; + js.block_flags = 0; + js.cancel = false; + + // Analyze the block, collect all instructions it is made of (including inlining, + // if that is enabled), reorder instructions for optimal performance, and join joinable instructions. + u32 nextPC = em_address; + u32 merged_addresses[32]; + const int capacity_of_merged_addresses = sizeof(merged_addresses) / sizeof(merged_addresses[0]); + int size_of_merged_addresses = 0; + if (!memory_exception) + { + // If there is a memory exception inside a block (broken_block==true), compile up to that instruction. + nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize, merged_addresses, capacity_of_merged_addresses, size_of_merged_addresses); + } + PPCAnalyst::CodeOp *ops = code_buf->codebuffer; + + const u8 *start = GetCodePtr(); + b->checkedEntry = start; + b->runCount = 0; + + // Downcount flag check, Only valid for linked blocks + { + // XXX + } + + const u8 *normalEntry = GetCodePtr(); + b->normalEntry = normalEntry; + + if (js.fpa.any) + { + // XXX + // This block uses FPU - needs to add FP exception bailout + } + js.rewriteStart = (u8*)GetCodePtr(); + + u64 codeHash = -1; + { + // For profiling and IR Writer + for (int i = 0; i < (int)size; i++) + { + const u64 inst = ops[i].inst.hex; + // Ported from boost::hash + codeHash ^= inst + (codeHash << 6) + (codeHash >> 2); + } + } + + // Conditionally add profiling code. + if (Profiler::g_ProfileBlocks) { + // XXX + } + // Start up IR builder (structure that collects the + // instruction processed by the JIT routines) + ibuild.Reset(); + + js.downcountAmount = 0; + if (!Core::g_CoreStartupParameter.bEnableDebugging) + { + for (int i = 0; i < size_of_merged_addresses; ++i) + { + const u32 address = merged_addresses[i]; + js.downcountAmount += PatchEngine::GetSpeedhackCycles(address); + } + } + + js.skipnext = false; + js.blockSize = size; + js.compilerPC = nextPC; + // Translate instructions + for (int i = 0; i < (int)size; i++) + { + js.compilerPC = ops[i].address; + js.op = &ops[i]; + js.instructionNumber = i; + const GekkoOPInfo *opinfo = ops[i].opinfo; + js.downcountAmount += (opinfo->numCyclesMinusOne + 1); + + if (i == (int)size - 1) + { + // WARNING - cmp->branch merging will screw this up. + js.isLastInstruction = true; + js.next_inst = 0; + if (Profiler::g_ProfileBlocks) { + // CAUTION!!! push on stack regs you use, do your stuff, then pop + PROFILER_VPUSH; + // get end tic + PROFILER_QUERY_PERFORMANCE_COUNTER(&b->ticStop); + // tic counter += (end tic - start tic) + PROFILER_ADD_DIFF_LARGE_INTEGER(&b->ticCounter, &b->ticStop, &b->ticStart); + PROFILER_VPOP; + } + } + else + { + // help peephole optimizations + js.next_inst = ops[i + 1].inst; + js.next_compilerPC = ops[i + 1].address; + } + if (!ops[i].skip) + { + PrintDebug(ops[i].inst, 1); + if (js.memcheck && (opinfo->flags & FL_USE_FPU)) + { + // Don't do this yet + BKPT(0x7777); + } + JitArmILTables::CompileInstruction(ops[i]); + if (js.memcheck && (opinfo->flags & FL_LOADSTORE)) + { + // Don't do this yet + BKPT(0x666); + } + } + } + if (memory_exception) + BKPT(0x500); + if (broken_block) + { + printf("Broken Block going to 0x%08x\n", nextPC); + WriteExit(nextPC, 0); + } + + // Perform actual code generation + + WriteCode(); + b->flags = js.block_flags; + b->codeSize = (u32)(GetCodePtr() - normalEntry); + b->originalSize = size; + + { + } + printf("Done emitting block. size was 0x%08x\n", size); + FlushIcache(); + return start; + +} diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.h b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.h new file mode 100644 index 0000000000..4c371a3b16 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL.h @@ -0,0 +1,104 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _JITARMIL_H +#define _JITARMIL_H + +#include "../PPCAnalyst.h" +#include "ArmEmitter.h" +#include "../JitArm32/JitArmCache.h" +#include "../JitILCommon/IR.h" +#include "../JitCommon/JitBase.h" +#include "JitILAsm.h" + +#define INSTRUCTION_START + +#define JITDISABLE(setting) \ + if (Core::g_CoreStartupParameter.bJITOff || \ + Core::g_CoreStartupParameter.setting) \ + {Default(inst); return;} + +#define PPCSTATE_OFF(elem) ((s32)STRUCT_OFF(PowerPC::ppcState, elem) - (s32)STRUCT_OFF(PowerPC::ppcState, spr[0])) +class JitArmIL : public JitBase, public ArmGen::ARMXCodeBlock +{ +private: + JitArmBlockCache blocks; + + // The default code buffer. We keep it around to not have to alloc/dealloc a + // large chunk of memory for each recompiled block. + PPCAnalyst::CodeBuffer code_buffer; + JitArmILAsmRoutineManager asm_routines; + + void PrintDebug(UGeckoInstruction inst, u32 level); + +public: + // Initialization, etc + JitArmIL() : code_buffer(32000) {} + ~JitArmIL() {} + + IREmitter::IRBuilder ibuild; + + void Init(); + void Shutdown(); + + // Jit! + + void Jit(u32 em_address); + const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buffer, JitBlock *b); + + JitBaseBlockCache *GetBlockCache() { return &blocks; } + + const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) {} + + bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); } + + void ClearCache(); + const u8 *GetDispatcher() { + return asm_routines.dispatcher; // asm_routines.dispatcher + } + const CommonAsmRoutinesBase *GetAsmRoutines() { + return &asm_routines; + } + + const char *GetName() { + return "JITARMIL"; + } + + // Run! + + void Run(); + void SingleStep(); + // + void WriteCode(); + void WriteExit(u32 destination, int exit_num); + void WriteExitDestInReg(ARMReg Reg); + void WriteRfiExitDestInR(ARMReg Reg); + + // OPCODES + void unknown_instruction(UGeckoInstruction inst); + void Default(UGeckoInstruction inst); + void DoNothing(UGeckoInstruction inst); + void HLEFunction(UGeckoInstruction inst); + + void DynaRunTable4(UGeckoInstruction inst); + void DynaRunTable19(UGeckoInstruction inst); + void DynaRunTable31(UGeckoInstruction inst); + void DynaRunTable59(UGeckoInstruction inst); + void DynaRunTable63(UGeckoInstruction inst); + + // Binary ops + void BIN_AND(ARMReg reg, Operand2 op2); + void BIN_XOR(ARMReg reg, Operand2 op2); + void BIN_ADD(ARMReg reg, Operand2 op2); + + // Branches + void rfi(UGeckoInstruction inst); + void bx(UGeckoInstruction inst); + void bcx(UGeckoInstruction inst); + void bclrx(UGeckoInstruction inst); + // System Registers + void mtmsr(UGeckoInstruction inst); +}; + +#endif diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.cpp new file mode 100644 index 0000000000..52157c4a64 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.cpp @@ -0,0 +1,121 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. +#include "../../HW/Memmap.h" + +#include "../PowerPC.h" +#include "../../CoreTiming.h" +#include "MemoryUtil.h" + +#include "JitIL.h" +#include "../JitCommon/JitCache.h" + +#include "../../HW/GPFifo.h" +#include "../../Core.h" + +#include "JitILAsm.h" +#include "ArmEmitter.h" + +JitArmILAsmRoutineManager armil_asm_routines; +void JitArmILAsmRoutineManager::Generate() +{ + enterCode = GetCodePtr(); + PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, _LR); + // Take care to 8-byte align stack for function calls. + // We are misaligned here because of an odd number of args for PUSH. + // It's not like x86 where you need to account for an extra 4 bytes + // consumed by CALL. + SUB(_SP, _SP, 4); + + MOVI2R(R0, (u32)&CoreTiming::downcount); + MOVI2R(R9, (u32)&PowerPC::ppcState.spr[0]); + + FixupBranch skipToRealDispatcher = B(); + dispatcher = GetCodePtr(); + printf("ILDispatcher is %p\n", dispatcher); + + // Downcount Check + // The result of slice decrementation should be in flags if somebody jumped here + // IMPORTANT - We jump on negative, not carry!!! + FixupBranch bail = B_CC(CC_MI); + + SetJumpTarget(skipToRealDispatcher); + dispatcherNoCheck = GetCodePtr(); + + // This block of code gets the address of the compiled block of code + // It runs though to the compiling portion if it isn't found + LDR(R12, R9, PPCSTATE_OFF(pc));// Load the current PC into R12 + + Operand2 iCacheMask = Operand2(0xE, 2); // JIT_ICACHE_MASK + BIC(R12, R12, iCacheMask); // R12 contains PC & JIT_ICACHE_MASK here. + + MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache()); + + LDR(R12, R14, R12); // R12 contains iCache[PC & JIT_ICACHE_MASK] here + // R12 Confirmed this is the correct iCache Location loaded. + TST(R12, 0xFC); // Test to see if it is a JIT block. + + SetCC(CC_EQ); + // Success, it is our Jitblock. + MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers()); + // LDR R14 right here to get CodePointers()[0] pointer. + REV(R12, R12); // Reversing this gives us our JITblock. + LSL(R12, R12, 2); // Multiply by four because address locations are u32 in size + LDR(R14, R14, R12); // Load the block address in to R14 + + B(R14); + // No need to jump anywhere after here, the block will go back to dispatcher start + SetCC(); + + // If we get to this point, that means that we don't have the block cached to execute + // So call ArmJit to compile the block and then execute it. + MOVI2R(R14, (u32)&Jit); + BL(R14); + + B(dispatcherNoCheck); + + // fpException() + // Floating Point Exception Check, Jumped to if false + fpException = GetCodePtr(); + LDR(R0, R9, PPCSTATE_OFF(Exceptions)); + ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE); + STR(R0, R9, PPCSTATE_OFF(Exceptions)); + QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); + LDR(R0, R9, PPCSTATE_OFF(npc)); + STR(R0, R9, PPCSTATE_OFF(pc)); + B(dispatcher); + + SetJumpTarget(bail); + doTiming = GetCodePtr(); + // XXX: In JIT64, Advance() gets called /after/ the exception checking + // once it jumps back to the start of outerLoop + QuickCallFunction(R14, (void*)&CoreTiming::Advance); + + // Does exception checking + testExceptions = GetCodePtr(); + LDR(R0, R9, PPCSTATE_OFF(pc)); + STR(R0, R9, PPCSTATE_OFF(npc)); + QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); + LDR(R0, R9, PPCSTATE_OFF(npc)); + STR(R0, R9, PPCSTATE_OFF(pc)); + // Check the state pointer to see if we are exiting + // Gets checked on every exception check + MOVI2R(R0, (u32)PowerPC::GetStatePtr()); + MVN(R1, 0); + LDR(R0, R0); + TST(R0, R1); + FixupBranch Exit = B_CC(CC_NEQ); + + B(dispatcher); + + SetJumpTarget(Exit); + + ADD(_SP, _SP, 4); + + POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns + + GenerateCommon(); + + FlushIcache(); +} + diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.h b/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.h new file mode 100644 index 0000000000..bd252f59f9 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitILAsm.h @@ -0,0 +1,32 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef _JITARMILASM_H +#define _JITARMILASM_H +#include "ArmEmitter.h" +#include "../JitCommon/JitAsmCommon.h" +using namespace ArmGen; +class JitArmILAsmRoutineManager : public CommonAsmRoutinesBase, public ARMXCodeBlock +{ +private: + void Generate(); + void GenerateCommon() {} + +public: + void Init() { + AllocCodeSpace(8192); + Generate(); + WriteProtect(); + } + + void Shutdown() { + FreeCodeSpace(); + } +}; + +extern JitArmILAsmRoutineManager armil_asm_routines; + +#endif + + diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Branch.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Branch.cpp new file mode 100644 index 0000000000..c6576e882d --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Branch.cpp @@ -0,0 +1,145 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common.h" + +#include "../../ConfigManager.h" +#include "../PowerPC.h" +#include "../PPCTables.h" + +#include "JitIL.h" + +#include "../../HW/Memmap.h" + +//#define NORMALBRANCH_START Default(inst); ibuild.EmitInterpreterBranch(); return; +#define NORMALBRANCH_START + +void JitArmIL::rfi(UGeckoInstruction inst) +{ + ibuild.EmitRFIExit(); +} + +void JitArmIL::bx(UGeckoInstruction inst) +{ + NORMALBRANCH_START + INSTRUCTION_START; + + // We must always process the following sentence + // even if the blocks are merged by PPCAnalyst::Flatten(). + if (inst.LK) + ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4)); + + // If this is not the last instruction of a block, + // we will skip the rest process. + // Because PPCAnalyst::Flatten() merged the blocks. + if (!js.isLastInstruction) { + return; + } + + u32 destination; + if (inst.AA) + destination = SignExt26(inst.LI << 2); + else + destination = js.compilerPC + SignExt26(inst.LI << 2); + + if (destination == js.compilerPC) { + ibuild.EmitShortIdleLoop(ibuild.EmitIntConst(js.compilerPC)); + return; + } + + ibuild.EmitBranchUncond(ibuild.EmitIntConst(destination)); +} +static IREmitter::InstLoc TestBranch(IREmitter::IRBuilder& ibuild, UGeckoInstruction inst) { + IREmitter::InstLoc CRTest = 0, CTRTest = 0; + if ((inst.BO & 16) == 0) // Test a CR bit + { + IREmitter::InstLoc CRReg = ibuild.EmitLoadCR(inst.BI >> 2); + IREmitter::InstLoc CRCmp = ibuild.EmitIntConst(8 >> (inst.BI & 3)); + CRTest = ibuild.EmitAnd(CRReg, CRCmp); + if (!(inst.BO & 8)) + CRTest = ibuild.EmitXor(CRCmp, CRTest); + } + + if ((inst.BO & 4) == 0) { + IREmitter::InstLoc c = ibuild.EmitLoadCTR(); + c = ibuild.EmitSub(c, ibuild.EmitIntConst(1)); + ibuild.EmitStoreCTR(c); + if (inst.BO & 2) { + CTRTest = ibuild.EmitICmpEq(c, + ibuild.EmitIntConst(0)); + } else { + CTRTest = c; + } + } + + IREmitter::InstLoc Test = CRTest; + if (CTRTest) { + if (Test) + Test = ibuild.EmitAnd(Test, CTRTest); + else + Test = CTRTest; + } + + if (!Test) { + Test = ibuild.EmitIntConst(1); + } + return Test; +} + +void JitArmIL::bclrx(UGeckoInstruction inst) +{ + NORMALBRANCH_START + + if (!js.isLastInstruction && + (inst.BO & (1 << 4)) && (inst.BO & (1 << 2))) { + if (inst.LK) + ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4)); + return; + } + + if (inst.hex == 0x4e800020) { + ibuild.EmitBranchUncond(ibuild.EmitLoadLink()); + return; + } + IREmitter::InstLoc test = TestBranch(ibuild, inst); + test = ibuild.EmitICmpEq(test, ibuild.EmitIntConst(0)); + ibuild.EmitBranchCond(test, ibuild.EmitIntConst(js.compilerPC + 4)); + + IREmitter::InstLoc destination = ibuild.EmitLoadLink(); + destination = ibuild.EmitAnd(destination, ibuild.EmitIntConst(-4)); + if (inst.LK) + ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4)); + ibuild.EmitBranchUncond(destination); +} +void JitArmIL::bcx(UGeckoInstruction inst) +{ + NORMALBRANCH_START + if (inst.LK) + ibuild.EmitStoreLink( + ibuild.EmitIntConst(js.compilerPC + 4)); + + IREmitter::InstLoc Test = TestBranch(ibuild, inst); + + u32 destination; + if(inst.AA) + destination = SignExt16(inst.BD << 2); + else + destination = js.compilerPC + SignExt16(inst.BD << 2); + + if (SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle && + inst.hex == 0x4182fff8 && + (Memory::ReadUnchecked_U32(js.compilerPC - 8) & 0xFFFF0000) == 0x800D0000 && + (Memory::ReadUnchecked_U32(js.compilerPC - 4) == 0x28000000 || + (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii && Memory::ReadUnchecked_U32(js.compilerPC - 4) == 0x2C000000)) + ) + { + ibuild.EmitIdleBranch(Test, ibuild.EmitIntConst(destination)); + } + else + { + ibuild.EmitBranchCond(Test, ibuild.EmitIntConst(destination)); + } + ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4)); +} + diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_SystemRegisters.cpp new file mode 100644 index 0000000000..f1ea5b6e8d --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_SystemRegisters.cpp @@ -0,0 +1,21 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common.h" + +#include "../PowerPC.h" +#include "../PPCTables.h" + +#include "JitIL.h" + +#include "../../HW/Memmap.h" + +// ======================================================================================= +// Don't interpret this, if we do we get thrown out +// -------------- +void JitArmIL::mtmsr(UGeckoInstruction inst) +{ + ibuild.EmitStoreMSR(ibuild.EmitLoadGReg(inst.RS), ibuild.EmitIntConst(js.compilerPC)); + ibuild.EmitBranchUncond(ibuild.EmitIntConst(js.compilerPC + 4)); +} diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.cpp b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.cpp new file mode 100644 index 0000000000..2072336188 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.cpp @@ -0,0 +1,489 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "JitIL.h" +#include "../JitInterface.h" +#include "JitIL_Tables.h" + +// Should be moved in to the Jit class +typedef void (JitArmIL::*_Instruction) (UGeckoInstruction instCode); + +static _Instruction dynaOpTable[64]; +static _Instruction dynaOpTable4[1024]; +static _Instruction dynaOpTable19[1024]; +static _Instruction dynaOpTable31[1024]; +static _Instruction dynaOpTable59[32]; +static _Instruction dynaOpTable63[1024]; + +void JitArmIL::DynaRunTable4(UGeckoInstruction _inst) {(this->*dynaOpTable4 [_inst.SUBOP10])(_inst);} +void JitArmIL::DynaRunTable19(UGeckoInstruction _inst) {(this->*dynaOpTable19[_inst.SUBOP10])(_inst);} +void JitArmIL::DynaRunTable31(UGeckoInstruction _inst) {(this->*dynaOpTable31[_inst.SUBOP10])(_inst);} +void JitArmIL::DynaRunTable59(UGeckoInstruction _inst) {(this->*dynaOpTable59[_inst.SUBOP5 ])(_inst);} +void JitArmIL::DynaRunTable63(UGeckoInstruction _inst) {(this->*dynaOpTable63[_inst.SUBOP10])(_inst);} + +struct GekkoOPTemplate +{ + int opcode; + _Instruction Inst; + //GekkoOPInfo opinfo; // Doesn't need opinfo, Interpreter fills it out +}; + +static GekkoOPTemplate primarytable[] = +{ + {4, &JitArmIL::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}}, + {19, &JitArmIL::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}}, + {31, &JitArmIL::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}}, + {59, &JitArmIL::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}}, + {63, &JitArmIL::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}}, + + {16, &JitArmIL::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {18, &JitArmIL::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + + {1, &JitArmIL::Default}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {2, &JitArmIL::Default}, //"DynaBlock", OPTYPE_SYSTEM, 0}}, + {3, &JitArmIL::Default}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {17, &JitArmIL::Default}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, + + {7, &JitArmIL::Default}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}}, + {8, &JitArmIL::Default}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}}, + {10, &JitArmIL::Default}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}}, + {11, &JitArmIL::Default}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}}, + {12, &JitArmIL::Default}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}}, + {13, &JitArmIL::Default}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}}, + {14, &JitArmIL::Default}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}}, + {15, &JitArmIL::Default}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}}, + + {20, &JitArmIL::Default}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}}, + {21, &JitArmIL::Default}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {23, &JitArmIL::Default}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}}, + + {24, &JitArmIL::Default}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {25, &JitArmIL::Default}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {26, &JitArmIL::Default}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {27, &JitArmIL::Default}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {28, &JitArmIL::Default}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}}, + {29, &JitArmIL::Default}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}}, + + {32, &JitArmIL::Default}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {33, &JitArmIL::Default}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {34, &JitArmIL::Default}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {35, &JitArmIL::Default}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {40, &JitArmIL::Default}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {41, &JitArmIL::Default}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {42, &JitArmIL::Default}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {43, &JitArmIL::Default}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + + {44, &JitArmIL::Default}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {45, &JitArmIL::Default}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + {36, &JitArmIL::Default}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {37, &JitArmIL::Default}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + {38, &JitArmIL::Default}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {39, &JitArmIL::Default}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + + {46, &JitArmIL::Default}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}}, + {47, &JitArmIL::Default}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}}, + + {48, &JitArmIL::Default}, //"lfs", OPTYPE_LOADFP, FL_IN_A}}, + {49, &JitArmIL::Default}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}}, + {50, &JitArmIL::Default}, //"lfd", OPTYPE_LOADFP, FL_IN_A}}, + {51, &JitArmIL::Default}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}}, + + {52, &JitArmIL::Default}, //"stfs", OPTYPE_STOREFP, FL_IN_A}}, + {53, &JitArmIL::Default}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}}, + {54, &JitArmIL::Default}, //"stfd", OPTYPE_STOREFP, FL_IN_A}}, + {55, &JitArmIL::Default}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}}, + + {56, &JitArmIL::Default}, //"psq_l", OPTYPE_PS, FL_IN_A}}, + {57, &JitArmIL::Default}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}}, + {60, &JitArmIL::Default}, //"psq_st", OPTYPE_PS, FL_IN_A}}, + {61, &JitArmIL::Default}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}}, + + //missing: 0, 5, 6, 9, 22, 30, 62, 58 + {0, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {5, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {6, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {9, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {22, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {30, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {62, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {58, &JitArmIL::Default}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, +}; + +static GekkoOPTemplate table4[] = +{ //SUBOP10 + {0, &JitArmIL::Default}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}}, + {32, &JitArmIL::Default}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}}, + {40, &JitArmIL::Default}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}}, + {136, &JitArmIL::Default}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}}, + {264, &JitArmIL::Default}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}}, + {64, &JitArmIL::Default}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}}, + {72, &JitArmIL::Default}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}}, + {96, &JitArmIL::Default}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}}, + {528, &JitArmIL::Default}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}}, + {560, &JitArmIL::Default}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}}, + {592, &JitArmIL::Default}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}}, + {624, &JitArmIL::Default}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}}, + + {1014, &JitArmIL::Default}, //"dcbz_l", OPTYPE_SYSTEM, 0}}, +}; + +static GekkoOPTemplate table4_2[] = +{ + {10, &JitArmIL::Default}, //"ps_sum0", OPTYPE_PS, 0}}, + {11, &JitArmIL::Default}, //"ps_sum1", OPTYPE_PS, 0}}, + {12, &JitArmIL::Default}, //"ps_muls0", OPTYPE_PS, 0}}, + {13, &JitArmIL::Default}, //"ps_muls1", OPTYPE_PS, 0}}, + {14, &JitArmIL::Default}, //"ps_madds0", OPTYPE_PS, 0}}, + {15, &JitArmIL::Default}, //"ps_madds1", OPTYPE_PS, 0}}, + {18, &JitArmIL::Default}, //"ps_div", OPTYPE_PS, 0, 16}}, + {20, &JitArmIL::Default}, //"ps_sub", OPTYPE_PS, 0}}, + {21, &JitArmIL::Default}, //"ps_add", OPTYPE_PS, 0}}, + {23, &JitArmIL::Default}, //"ps_sel", OPTYPE_PS, 0}}, + {24, &JitArmIL::Default}, //"ps_res", OPTYPE_PS, 0}}, + {25, &JitArmIL::Default}, //"ps_mul", OPTYPE_PS, 0}}, + {26, &JitArmIL::Default}, //"ps_rsqrte", OPTYPE_PS, 0, 1}}, + {28, &JitArmIL::Default}, //"ps_msub", OPTYPE_PS, 0}}, + {29, &JitArmIL::Default}, //"ps_madd", OPTYPE_PS, 0}}, + {30, &JitArmIL::Default}, //"ps_nmsub", OPTYPE_PS, 0}}, + {31, &JitArmIL::Default}, //"ps_nmadd", OPTYPE_PS, 0}}, +}; + + +static GekkoOPTemplate table4_3[] = +{ + {6, &JitArmIL::Default}, //"psq_lx", OPTYPE_PS, 0}}, + {7, &JitArmIL::Default}, //"psq_stx", OPTYPE_PS, 0}}, + {38, &JitArmIL::Default}, //"psq_lux", OPTYPE_PS, 0}}, + {39, &JitArmIL::Default}, //"psq_stux", OPTYPE_PS, 0}}, +}; + +static GekkoOPTemplate table19[] = +{ + {528, &JitArmIL::Default}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {16, &JitArmIL::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {257, &JitArmIL::Default}, //"crand", OPTYPE_CR, FL_EVIL}}, + {129, &JitArmIL::Default}, //"crandc", OPTYPE_CR, FL_EVIL}}, + {289, &JitArmIL::Default}, //"creqv", OPTYPE_CR, FL_EVIL}}, + {225, &JitArmIL::Default}, //"crnand", OPTYPE_CR, FL_EVIL}}, + {33, &JitArmIL::Default}, //"crnor", OPTYPE_CR, FL_EVIL}}, + {449, &JitArmIL::Default}, //"cror", OPTYPE_CR, FL_EVIL}}, + {417, &JitArmIL::Default}, //"crorc", OPTYPE_CR, FL_EVIL}}, + {193, &JitArmIL::Default}, //"crxor", OPTYPE_CR, FL_EVIL}}, + + {150, &JitArmIL::Default}, //"isync", OPTYPE_ICACHE, FL_EVIL}}, + {0, &JitArmIL::Default}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}}, + + {50, &JitArmIL::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}}, + {18, &JitArmIL::Default}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}} +}; + + +static GekkoOPTemplate table31[] = +{ + {28, &JitArmIL::Default}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {60, &JitArmIL::Default}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {444, &JitArmIL::Default}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {124, &JitArmIL::Default}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {316, &JitArmIL::Default}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {412, &JitArmIL::Default}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {476, &JitArmIL::Default}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {284, &JitArmIL::Default}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {0, &JitArmIL::Default}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {32, &JitArmIL::Default}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {26, &JitArmIL::Default}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {922, &JitArmIL::Default}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {954, &JitArmIL::Default}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {536, &JitArmIL::Default}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {792, &JitArmIL::Default}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {824, &JitArmIL::Default}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {24, &JitArmIL::Default}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + + {54, &JitArmIL::Default}, //"dcbst", OPTYPE_DCACHE, 0, 4}}, + {86, &JitArmIL::Default}, //"dcbf", OPTYPE_DCACHE, 0, 4}}, + {246, &JitArmIL::Default}, //"dcbtst", OPTYPE_DCACHE, 0, 1}}, + {278, &JitArmIL::Default}, //"dcbt", OPTYPE_DCACHE, 0, 1}}, + {470, &JitArmIL::Default}, //"dcbi", OPTYPE_DCACHE, 0, 4}}, + {758, &JitArmIL::Default}, //"dcba", OPTYPE_DCACHE, 0, 4}}, + {1014, &JitArmIL::Default}, //"dcbz", OPTYPE_DCACHE, 0, 4}}, + + //load word + {23, &JitArmIL::Default}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {55, &JitArmIL::Default}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load halfword + {279, &JitArmIL::Default}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {311, &JitArmIL::Default}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load halfword signextend + {343, &JitArmIL::Default}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {375, &JitArmIL::Default}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load byte + {87, &JitArmIL::Default}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {119, &JitArmIL::Default}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load byte reverse + {534, &JitArmIL::Default}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {790, &JitArmIL::Default}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + + // Conditional load/store (Wii SMP) + {150, &JitArmIL::Default}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}}, + {20, &JitArmIL::Default}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}}, + + //load string (interpret these) + {533, &JitArmIL::Default}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}}, + {597, &JitArmIL::Default}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}}, + + //store word + {151, &JitArmIL::Default}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {183, &JitArmIL::Default}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store halfword + {407, &JitArmIL::Default}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {439, &JitArmIL::Default}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store byte + {215, &JitArmIL::Default}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {247, &JitArmIL::Default}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store bytereverse + {662, &JitArmIL::Default}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {918, &JitArmIL::Default}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + {661, &JitArmIL::Default}, //"stswx", OPTYPE_STORE, FL_EVIL}}, + {725, &JitArmIL::Default}, //"stswi", OPTYPE_STORE, FL_EVIL}}, + + // fp load/store + {535, &JitArmIL::Default}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {567, &JitArmIL::Default}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + {599, &JitArmIL::Default}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {631, &JitArmIL::Default}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + + {663, &JitArmIL::Default}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {695, &JitArmIL::Default}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {727, &JitArmIL::Default}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {759, &JitArmIL::Default}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {983, &JitArmIL::Default}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + + {19, &JitArmIL::Default}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}}, + {83, &JitArmIL::Default}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}}, + {144, &JitArmIL::Default}, //"mtcrf", OPTYPE_SYSTEM, 0}}, + {146, &JitArmIL::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {210, &JitArmIL::Default}, //"mtsr", OPTYPE_SYSTEM, 0}}, + {242, &JitArmIL::Default}, //"mtsrin", OPTYPE_SYSTEM, 0}}, + {339, &JitArmIL::Default}, //"mfspr", OPTYPE_SPR, FL_OUT_D}}, + {467, &JitArmIL::Default}, //"mtspr", OPTYPE_SPR, 0, 2}}, + {371, &JitArmIL::Default}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}}, + {512, &JitArmIL::Default}, //"mcrxr", OPTYPE_SYSTEM, 0}}, + {595, &JitArmIL::Default}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}}, + {659, &JitArmIL::Default}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}}, + + {4, &JitArmIL::Default}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, + {598, &JitArmIL::Default}, //"sync", OPTYPE_SYSTEM, 0, 2}}, + {982, &JitArmIL::Default}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}}, + + // Unused instructions on GC + {310, &JitArmIL::Default}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}}, + {438, &JitArmIL::Default}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}}, + {854, &JitArmIL::Default}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}}, + {306, &JitArmIL::Default}, //"tlbie", OPTYPE_SYSTEM, 0}}, + {370, &JitArmIL::Default}, //"tlbia", OPTYPE_SYSTEM, 0}}, + {566, &JitArmIL::Default}, //"tlbsync", OPTYPE_SYSTEM, 0}}, +}; + +static GekkoOPTemplate table31_2[] = +{ + {266, &JitArmIL::Default}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {778, &JitArmIL::Default}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {10, &JitArmIL::Default}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {138, &JitArmIL::Default}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {234, &JitArmIL::Default}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {202, &JitArmIL::Default}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {491, &JitArmIL::Default}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {1003, &JitArmIL::Default}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {459, &JitArmIL::Default}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {971, &JitArmIL::Default}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {75, &JitArmIL::Default}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {11, &JitArmIL::Default}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {235, &JitArmIL::Default}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {747, &JitArmIL::Default}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {104, &JitArmIL::Default}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {40, &JitArmIL::Default}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {552, &JitArmIL::Default}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {8, &JitArmIL::Default}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {136, &JitArmIL::Default}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {232, &JitArmIL::Default}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {200, &JitArmIL::Default}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, +}; + +static GekkoOPTemplate table59[] = +{ + {18, &JitArmIL::Default}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}}, + {20, &JitArmIL::Default}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, &JitArmIL::Default}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +// {22, &JitArmIL::Default}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {24, &JitArmIL::Default}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, &JitArmIL::Default}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, &JitArmIL::Default}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, &JitArmIL::Default}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, &JitArmIL::Default}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, &JitArmIL::Default}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + +static GekkoOPTemplate table63[] = +{ + {264, &JitArmIL::Default}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {32, &JitArmIL::Default}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}}, + {0, &JitArmIL::Default}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}}, + {14, &JitArmIL::Default}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}}, + {15, &JitArmIL::Default}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}}, + {72, &JitArmIL::Default}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}}, + {136, &JitArmIL::Default}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {40, &JitArmIL::Default}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}}, + {12, &JitArmIL::Default}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}}, + + {64, &JitArmIL::Default}, //"mcrfs", OPTYPE_SYSTEMFP, 0}}, + {583, &JitArmIL::Default}, //"mffsx", OPTYPE_SYSTEMFP, 0}}, + {70, &JitArmIL::Default}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}}, + {38, &JitArmIL::Default}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}}, + {134, &JitArmIL::Default}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}}, + {711, &JitArmIL::Default}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}}, +}; + +static GekkoOPTemplate table63_2[] = +{ + {18, &JitArmIL::Default}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}}, + {20, &JitArmIL::Default}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, &JitArmIL::Default}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {22, &JitArmIL::Default}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}}, + {23, &JitArmIL::Default}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, &JitArmIL::Default}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}}, + {26, &JitArmIL::Default}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, &JitArmIL::Default}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, &JitArmIL::Default}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, &JitArmIL::Default}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, &JitArmIL::Default}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + + +namespace JitArmILTables +{ + +void CompileInstruction(PPCAnalyst::CodeOp & op) +{ + JitArmIL *jitarm = (JitArmIL *)jit; + (jitarm->*dynaOpTable[op.inst.OPCD])(op.inst); + GekkoOPInfo *info = op.opinfo; + if (info) { +#ifdef OPLOG + if (!strcmp(info->opname, OP_TO_LOG)){ ///"mcrfs" + rsplocations.push_back(jit.js.compilerPC); + } +#endif + info->compileCount++; + info->lastUse = jit->js.compilerPC; + } +} + +void InitTables() +{ + // once initialized, tables are read-only + static bool initialized = false; + if (initialized) + return; + + //clear + for (int i = 0; i < 32; i++) + { + dynaOpTable59[i] = &JitArmIL::unknown_instruction; + } + + for (int i = 0; i < 1024; i++) + { + dynaOpTable4 [i] = &JitArmIL::unknown_instruction; + dynaOpTable19[i] = &JitArmIL::unknown_instruction; + dynaOpTable31[i] = &JitArmIL::unknown_instruction; + dynaOpTable63[i] = &JitArmIL::unknown_instruction; + } + + for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++) + { + dynaOpTable[primarytable[i].opcode] = primarytable[i].Inst; + } + + for (int i = 0; i < 32; i++) + { + int fill = i << 5; + for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill+table4_2[j].opcode; + dynaOpTable4[op] = table4_2[j].Inst; + } + } + + for (int i = 0; i < 16; i++) + { + int fill = i << 6; + for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill+table4_3[j].opcode; + dynaOpTable4[op] = table4_3[j].Inst; + } + } + + for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++) + { + int op = table4[i].opcode; + dynaOpTable4[op] = table4[i].Inst; + } + + for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++) + { + int op = table31[i].opcode; + dynaOpTable31[op] = table31[i].Inst; + } + + for (int i = 0; i < 1; i++) + { + int fill = i << 9; + for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill + table31_2[j].opcode; + dynaOpTable31[op] = table31_2[j].Inst; + } + } + + for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++) + { + int op = table19[i].opcode; + dynaOpTable19[op] = table19[i].Inst; + } + + for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++) + { + int op = table59[i].opcode; + dynaOpTable59[op] = table59[i].Inst; + } + + for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++) + { + int op = table63[i].opcode; + dynaOpTable63[op] = table63[i].Inst; + } + + for (int i = 0; i < 32; i++) + { + int fill = i << 5; + for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill + table63_2[j].opcode; + dynaOpTable63[op] = table63_2[j].Inst; + } + } + + initialized = true; + +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.h b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.h new file mode 100644 index 0000000000..9402b9d444 --- /dev/null +++ b/Source/Core/Core/Src/PowerPC/JitArmIL/JitIL_Tables.h @@ -0,0 +1,16 @@ +// Copyright 2013 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#ifndef JITARMIL_TABLES_H +#define JITARMIL_TABLES_H + +#include "../Gekko.h" +#include "../PPCTables.h" + +namespace JitArmILTables +{ + void CompileInstruction(PPCAnalyst::CodeOp & op); + void InitTables(); +} +#endif diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp b/Source/Core/Core/Src/PowerPC/JitILCommon/IR.cpp similarity index 99% rename from Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp rename to Source/Core/Core/Src/PowerPC/JitILCommon/IR.cpp index 6ffdbad7ad..50cba22a89 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.cpp +++ b/Source/Core/Core/Src/PowerPC/JitILCommon/IR.cpp @@ -124,8 +124,6 @@ Fix profiled loads/stores to work safely. On 32-bit, one solution is to #include "../PPCTables.h" #include "../../CoreTiming.h" #include "../../HW/Memmap.h" -#include "JitILAsm.h" -#include "JitIL.h" #include "../../HW/GPFifo.h" #include "../../Core.h" using namespace Gen; diff --git a/Source/Core/Core/Src/PowerPC/Jit64IL/IR.h b/Source/Core/Core/Src/PowerPC/JitILCommon/IR.h similarity index 100% rename from Source/Core/Core/Src/PowerPC/Jit64IL/IR.h rename to Source/Core/Core/Src/PowerPC/JitILCommon/IR.h diff --git a/Source/Core/Core/Src/PowerPC/JitInterface.cpp b/Source/Core/Core/Src/PowerPC/JitInterface.cpp index f5158dad6f..6ab3a20bab 100644 --- a/Source/Core/Core/Src/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/Src/PowerPC/JitInterface.cpp @@ -21,6 +21,8 @@ #ifdef _M_ARM #include "JitArm32/Jit.h" #include "JitArm32/JitArm_Tables.h" +#include "JitArmIL/JitIL.h" +#include "JitArmIL/JitIL_Tables.h" #endif #include "Profiler.h" @@ -64,6 +66,11 @@ namespace JitInterface ptr = new JitArm(); break; } + case 4: + { + ptr = new JitArmIL(); + break; + } #endif default: { @@ -99,6 +106,11 @@ namespace JitInterface JitArmTables::InitTables(); break; } + case 4: + { + JitArmILTables::InitTables(); + break; + } #endif default: {