| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,365 @@ | ||
| // Copyright 2013 Dolphin Emulator Project | ||
| // Licensed under GPLv2 | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #include <map> | ||
|
|
||
| #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::Break(UGeckoInstruction _inst) | ||
| { | ||
| ibuild.EmitINT3(); | ||
| } | ||
|
|
||
| void JitArmIL::DoDownCount() | ||
| { | ||
| ARMReg rA = R14; | ||
| ARMReg rB = R12; | ||
| MOVI2R(rA, (u32)&CoreTiming::downcount); | ||
| LDR(rB, rA); | ||
| if(js.downcountAmount < 255) // We can enlarge this if we used rotations | ||
| { | ||
| SUBS(rB, rB, js.downcountAmount); | ||
| STR(rB, rA); | ||
| } | ||
| else | ||
| { | ||
| ARMReg rC = R11; | ||
| MOVI2R(rC, js.downcountAmount); | ||
| SUBS(rB, rB, rC); | ||
| STR(rB, rA); | ||
| } | ||
| } | ||
|
|
||
| void JitArmIL::WriteExitDestInReg(ARMReg Reg) | ||
| { | ||
| STR(Reg, R9, PPCSTATE_OFF(pc)); | ||
| DoDownCount(); | ||
| MOVI2R(Reg, (u32)asm_routines.dispatcher); | ||
| B(Reg); | ||
| } | ||
|
|
||
| void JitArmIL::WriteRfiExitDestInR(ARMReg Reg) | ||
| { | ||
| STR(Reg, R9, PPCSTATE_OFF(pc)); | ||
| DoDownCount(); | ||
| MOVI2R(Reg, (u32)asm_routines.testExceptions); | ||
| B(Reg); | ||
| } | ||
| void JitArmIL::WriteExceptionExit() | ||
| { | ||
| DoDownCount(); | ||
|
|
||
| MOVI2R(R14, (u32)asm_routines.testExceptions); | ||
| B(R14); | ||
| } | ||
| void JitArmIL::WriteExit(u32 destination, int exit_num) | ||
| { | ||
| DoDownCount(); | ||
| //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) | ||
| { | ||
| 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, 0); | ||
| 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; | ||
|
|
||
| { | ||
| } | ||
| FlushIcache(); | ||
| return start; | ||
|
|
||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,99 @@ | ||
| // 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/JitILBase.h" | ||
| #include "../JitILCommon/IR.h" | ||
| #include "../JitCommon/JitBase.h" | ||
| #include "JitILAsm.h" | ||
|
|
||
| #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 JitILBase, public ArmGen::ARMXCodeBlock | ||
| { | ||
| private: | ||
| JitArmBlockCache blocks; | ||
| JitArmILAsmRoutineManager asm_routines; | ||
|
|
||
| void PrintDebug(UGeckoInstruction inst, u32 level); | ||
| void DoDownCount(); | ||
|
|
||
| public: | ||
| // Initialization, etc | ||
| JitArmIL() {} | ||
| ~JitArmIL() {} | ||
|
|
||
| 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) { return NULL; } | ||
|
|
||
| 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); | ||
| void WriteExceptionExit(); | ||
|
|
||
| // OPCODES | ||
| void unknown_instruction(UGeckoInstruction inst); | ||
| void Default(UGeckoInstruction inst); | ||
| void DoNothing(UGeckoInstruction inst); | ||
| void HLEFunction(UGeckoInstruction inst); | ||
| void Break(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_OR(ARMReg reg, Operand2 op2); | ||
| void BIN_ADD(ARMReg reg, Operand2 op2); | ||
|
|
||
| // Branches | ||
| void bx(UGeckoInstruction inst); | ||
| void bcx(UGeckoInstruction inst); | ||
| void bclrx(UGeckoInstruction inst); | ||
| void bcctrx(UGeckoInstruction inst); | ||
| }; | ||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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(); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
|
|
||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,146 @@ | ||
| // Copyright 2013 Dolphin Emulator Project | ||
| // Licensed under GPLv2 | ||
| // Refer to the license.txt file included. | ||
|
|
||
| #ifndef _JITILBASE_H | ||
| #define _JITILBASE_H | ||
|
|
||
| #include "../PPCAnalyst.h" | ||
| #include "IR.h" | ||
| #include "../JitCommon/JitBase.h" | ||
|
|
||
| #define INSTRUCTION_START | ||
|
|
||
| #define JITDISABLE(setting) \ | ||
| if (Core::g_CoreStartupParameter.bJITOff || \ | ||
| Core::g_CoreStartupParameter.setting) \ | ||
| {Default(inst); return;} | ||
|
|
||
| class JitILBase : public JitBase | ||
| { | ||
| protected: | ||
| // 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; | ||
| public: | ||
| JitILBase() : code_buffer(32000) {} | ||
| ~JitILBase() {} | ||
|
|
||
| IREmitter::IRBuilder ibuild; | ||
|
|
||
| virtual JitBaseBlockCache *GetBlockCache() = 0; | ||
|
|
||
| virtual void Jit(u32 em_address) = 0; | ||
|
|
||
| virtual const u8 *BackPatch(u8 *codePtr, u32 em_address, void *ctx) = 0; | ||
|
|
||
| virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0; | ||
|
|
||
| virtual bool IsInCodeSpace(u8 *ptr) = 0; | ||
|
|
||
| // OPCODES | ||
| virtual void unknown_instruction(UGeckoInstruction inst) = 0; | ||
| virtual void Default(UGeckoInstruction inst) = 0; | ||
| virtual void DoNothing(UGeckoInstruction inst) = 0; | ||
| virtual void HLEFunction(UGeckoInstruction inst) = 0; | ||
|
|
||
| virtual void DynaRunTable4(UGeckoInstruction _inst) = 0; | ||
| virtual void DynaRunTable19(UGeckoInstruction _inst) = 0; | ||
| virtual void DynaRunTable31(UGeckoInstruction _inst) = 0; | ||
| virtual void DynaRunTable59(UGeckoInstruction _inst) = 0; | ||
| virtual void DynaRunTable63(UGeckoInstruction _inst) = 0; | ||
|
|
||
| // Branches | ||
| void sc(UGeckoInstruction inst); | ||
| void rfi(UGeckoInstruction inst); | ||
| void bx(UGeckoInstruction inst); | ||
| void bcx(UGeckoInstruction inst); | ||
| void bcctrx(UGeckoInstruction inst); | ||
| void bclrx(UGeckoInstruction inst); | ||
|
|
||
| // LoadStore | ||
| void lXzx(UGeckoInstruction inst); | ||
| void lhax(UGeckoInstruction inst); | ||
| void stXx(UGeckoInstruction inst); | ||
| void lmw(UGeckoInstruction inst); | ||
| void stmw(UGeckoInstruction inst); | ||
| void stX(UGeckoInstruction inst); //stw sth stb | ||
| void lXz(UGeckoInstruction inst); | ||
| void lbzu(UGeckoInstruction inst); | ||
| void lha(UGeckoInstruction inst); | ||
|
|
||
| // System Registers | ||
| void mtspr(UGeckoInstruction inst); | ||
| void mfspr(UGeckoInstruction inst); | ||
| void mtmsr(UGeckoInstruction inst); | ||
| void mfmsr(UGeckoInstruction inst); | ||
| void mftb(UGeckoInstruction inst); | ||
| void mtcrf(UGeckoInstruction inst); | ||
| void mfcr(UGeckoInstruction inst); | ||
| void mcrf(UGeckoInstruction inst); | ||
| void crXX(UGeckoInstruction inst); | ||
|
|
||
| void dcbst(UGeckoInstruction inst); | ||
| void dcbz(UGeckoInstruction inst); | ||
| void icbi(UGeckoInstruction inst); | ||
|
|
||
| void addx(UGeckoInstruction inst); | ||
| void boolX(UGeckoInstruction inst); | ||
| void mulli(UGeckoInstruction inst); | ||
| void mulhwux(UGeckoInstruction inst); | ||
| void mullwx(UGeckoInstruction inst); | ||
| void divwux(UGeckoInstruction inst); | ||
| void srawix(UGeckoInstruction inst); | ||
| void srawx(UGeckoInstruction inst); | ||
| void addex(UGeckoInstruction inst); | ||
| void addzex(UGeckoInstruction inst); | ||
|
|
||
| void extsbx(UGeckoInstruction inst); | ||
| void extshx(UGeckoInstruction inst); | ||
|
|
||
| void reg_imm(UGeckoInstruction inst); | ||
|
|
||
| void ps_sel(UGeckoInstruction inst); | ||
| void ps_mr(UGeckoInstruction inst); | ||
| void ps_sign(UGeckoInstruction inst); //aggregate | ||
| void ps_arith(UGeckoInstruction inst); //aggregate | ||
| void ps_mergeXX(UGeckoInstruction inst); | ||
| void ps_maddXX(UGeckoInstruction inst); | ||
| void ps_rsqrte(UGeckoInstruction inst); | ||
| void ps_sum(UGeckoInstruction inst); | ||
| void ps_muls(UGeckoInstruction inst); | ||
|
|
||
| void fp_arith_s(UGeckoInstruction inst); | ||
|
|
||
| void fcmpx(UGeckoInstruction inst); | ||
| void fmrx(UGeckoInstruction inst); | ||
|
|
||
| void cmpXX(UGeckoInstruction inst); | ||
|
|
||
| void cntlzwx(UGeckoInstruction inst); | ||
|
|
||
| void lfs(UGeckoInstruction inst); | ||
| void lfd(UGeckoInstruction inst); | ||
| void stfd(UGeckoInstruction inst); | ||
| void stfs(UGeckoInstruction inst); | ||
| void stfsx(UGeckoInstruction inst); | ||
| void psq_l(UGeckoInstruction inst); | ||
| void psq_st(UGeckoInstruction inst); | ||
|
|
||
| void fmaddXX(UGeckoInstruction inst); | ||
| void fsign(UGeckoInstruction inst); | ||
| void rlwinmx(UGeckoInstruction inst); | ||
| void rlwimix(UGeckoInstruction inst); | ||
| void rlwnmx(UGeckoInstruction inst); | ||
| void negx(UGeckoInstruction inst); | ||
| void slwx(UGeckoInstruction inst); | ||
| void srwx(UGeckoInstruction inst); | ||
| void lfsx(UGeckoInstruction inst); | ||
|
|
||
| void subfic(UGeckoInstruction inst); | ||
| void subfcx(UGeckoInstruction inst); | ||
| void subfx(UGeckoInstruction inst); | ||
| void subfex(UGeckoInstruction inst); | ||
|
|
||
| }; | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,191 @@ | ||
| // 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 "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "JitILBase.h" | ||
|
|
||
| #include "../../HW/Memmap.h" | ||
|
|
||
| // The branches are known good, or at least reasonably good. | ||
| // No need for a disable-mechanism. | ||
|
|
||
| // If defined, clears CR0 at blr and bl-s. If the assumption that | ||
| // flags never carry over between functions holds, then the task for | ||
| // an optimizer becomes much easier. | ||
|
|
||
| // #define ACID_TEST | ||
|
|
||
| // Zelda and many more games seem to pass the Acid Test. | ||
|
|
||
| //#define NORMALBRANCH_START Default(inst); ibuild.EmitInterpreterBranch(); return; | ||
| #define NORMALBRANCH_START | ||
|
|
||
| void JitILBase::sc(UGeckoInstruction inst) | ||
| { | ||
| ibuild.EmitSystemCall(ibuild.EmitIntConst(js.compilerPC)); | ||
| } | ||
|
|
||
| void JitILBase::rfi(UGeckoInstruction inst) | ||
| { | ||
| ibuild.EmitRFIExit(); | ||
| } | ||
|
|
||
| void JitILBase::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 JitILBase::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)); | ||
| } | ||
|
|
||
| void JitILBase::bcctrx(UGeckoInstruction inst) | ||
| { | ||
| NORMALBRANCH_START | ||
| if ((inst.BO & 4) == 0) { | ||
| IREmitter::InstLoc c = ibuild.EmitLoadCTR(); | ||
| c = ibuild.EmitSub(c, ibuild.EmitIntConst(1)); | ||
| ibuild.EmitStoreCTR(c); | ||
| } | ||
| IREmitter::InstLoc test; | ||
| 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)); | ||
| test = ibuild.EmitAnd(CRReg, CRCmp); | ||
| if (!(inst.BO & 8)) | ||
| test = ibuild.EmitXor(test, CRCmp); | ||
| } else { | ||
| test = ibuild.EmitIntConst(1); | ||
| } | ||
| test = ibuild.EmitICmpEq(test, ibuild.EmitIntConst(0)); | ||
| ibuild.EmitBranchCond(test, ibuild.EmitIntConst(js.compilerPC + 4)); | ||
|
|
||
| IREmitter::InstLoc destination = ibuild.EmitLoadCTR(); | ||
| destination = ibuild.EmitAnd(destination, ibuild.EmitIntConst(-4)); | ||
| if (inst.LK) | ||
| ibuild.EmitStoreLink(ibuild.EmitIntConst(js.compilerPC + 4)); | ||
| ibuild.EmitBranchUncond(destination); | ||
| } | ||
|
|
||
| void JitILBase::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); | ||
| } |