diff --git a/Source/Core/Common/CMakeLists.txt b/Source/Core/Common/CMakeLists.txt index aebfc3e7ea..cfdf6cd14c 100644 --- a/Source/Core/Common/CMakeLists.txt +++ b/Source/Core/Common/CMakeLists.txt @@ -31,19 +31,23 @@ set(SRCS BreakPoints.cpp Logging/LogManager.cpp) -if(_M_ARM_32) #ARMv7 - set(SRCS ${SRCS} - ArmCPUDetect.cpp - ArmEmitter.cpp - GenericFPURoundMode.cpp) -elseif(_M_X86) #X86 - set(SRCS ${SRCS} - x64CPUDetect.cpp - x64FPURoundMode.cpp) -else() #Generic - set(SRCS ${SRCS} - GenericFPURoundMode.cpp - x64CPUDetect.cpp) +if(_M_ARM) + if (_M_ARM_32) #ARMv7 + set(SRCS ${SRCS} + ArmEmitter.cpp) + else() #AArch64 + set(SRCS ${SRCS} + Arm64Emitter.cpp) + endif() + set(SRCS ${SRCS} + ArmCPUDetect.cpp + GenericFPURoundMode.cpp) +else() + if(_M_X86) #X86 + set(SRCS ${SRCS} + x64FPURoundMode.cpp) + endif() + set(SRCS ${SRCS} x64CPUDetect.cpp) endif() if(WIN32) set(SRCS ${SRCS} ExtendedTrace.cpp) diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 55669cfb0f..13235e3a73 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -197,8 +197,7 @@ if(_M_X86) PowerPC/JitCommon/JitBackpatch.cpp PowerPC/JitCommon/JitAsmCommon.cpp PowerPC/JitCommon/Jit_Util.cpp) -endif() -if(_M_ARM_32) +elseif(_M_ARM_32) set(SRCS ${SRCS} ArmMemTools.cpp PowerPC/JitArm32/Jit.cpp @@ -217,6 +216,16 @@ if(_M_ARM_32) PowerPC/JitArm32/JitArm_SystemRegisters.cpp PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp ) +elseif(_M_ARM_64) + set(SRCS ${SRCS} + PowerPC/JitArm64/Jit.cpp + PowerPC/JitArm64/JitAsm.cpp + PowerPC/JitArm64/JitArm64Cache.cpp + PowerPC/JitArm64/JitArm64_RegCache.cpp + PowerPC/JitArm64/JitArm64_Branch.cpp + PowerPC/JitArm64/JitArm64_LoadStore.cpp + PowerPC/JitArm64/JitArm64_SystemRegisters.cpp + PowerPC/JitArm64/JitArm64_Tables.cpp) endif() set(LIBS diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.cpp b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp new file mode 100644 index 0000000000..f8322a5438 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.cpp @@ -0,0 +1,287 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/PatchEngine.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" +#include "Core/PowerPC/JitArm64/JitArm64_Tables.h" + +using namespace Arm64Gen; + +static int CODE_SIZE = 1024*1024*32; + +void JitArm64::Init() +{ + AllocCodeSpace(CODE_SIZE); + jo.enableBlocklink = true; + gpr.Init(this); + fpr.Init(this); + + blocks.Init(); + asm_routines.Init(); + + code_block.m_stats = &js.st; + code_block.m_gpa = &js.gpa; + code_block.m_fpa = &js.fpa; +} + +void JitArm64::ClearCache() +{ + ClearCodeSpace(); + blocks.Clear(); +} + +void JitArm64::Shutdown() +{ + FreeCodeSpace(); + blocks.Shutdown(); + asm_routines.Shutdown(); +} + +void JitArm64::unknown_instruction(UGeckoInstruction inst) +{ + WARN_LOG(DYNA_REC, "unknown_instruction %08x - Fix me ;)", inst.hex); +} + +void JitArm64::FallBackToInterpreter(UGeckoInstruction inst) +{ + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + Interpreter::_interpreterInstruction instr = GetInterpreterOp(inst); + MOVI2R(W0, inst.hex); + MOVI2R(X30, (u64)instr); + BLR(X30); +} + +void JitArm64::HLEFunction(UGeckoInstruction inst) +{ + WARN_LOG(DYNA_REC, "HLEFunction %08x - Fix me ;)", inst.hex); + exit(0); +} + +void JitArm64::DoNothing(UGeckoInstruction inst) +{ + // Yup, just don't do anything. +} + +void JitArm64::Break(UGeckoInstruction inst) +{ + WARN_LOG(DYNA_REC, "Breaking! %08x - Fix me ;)", inst.hex); + exit(0); +} + +void JitArm64::DoDownCount() +{ + ARM64Reg WA = gpr.GetReg(); + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount)); + if (js.downcountAmount < 4096) // We can enlarge this if we used rotations + { + SUBS(WA, WA, js.downcountAmount); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount)); + } + else + { + ARM64Reg WB = gpr.GetReg(); + MOVI2R(WB, js.downcountAmount); + SUBS(WA, WA, WB); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(downcount)); + gpr.Unlock(WB); + } + gpr.Unlock(WA); +} + +// Exits +void JitArm64::WriteExit(u32 destination) +{ + //If nobody has taken care of this yet (this can be removed when all branches are done) + JitBlock *b = js.curBlock; + JitBlock::LinkData linkData; + linkData.exitAddress = destination; + linkData.exitPtrs = GetWritableCodePtr(); + linkData.linkStatus = false; + + DoDownCount(); + + // Link opportunity! + int block; + if (jo.enableBlocklink && (block = blocks.GetBlockNumberFromStartAddress(destination)) >= 0) + { + // It exists! Joy of joy! + B(blocks.GetBlock(block)->checkedEntry); + linkData.linkStatus = true; + } + else + { + ARM64Reg WA = gpr.GetReg(); + ARM64Reg XA = EncodeRegTo64(WA); + MOVI2R(WA, destination); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc)); + MOVI2R(XA, (u64)asm_routines.dispatcher); + BR(XA); + gpr.Unlock(WA); + } + + b->linkData.push_back(linkData); +} +void JitArm64::WriteExceptionExit(ARM64Reg dest) +{ + STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(pc)); + STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(npc)); + gpr.Unlock(dest); + DoDownCount(); + MOVI2R(EncodeRegTo64(dest), (u64)&PowerPC::CheckExceptions); + BLR(EncodeRegTo64(dest)); + LDR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(npc)); + STR(INDEX_UNSIGNED, dest, X29, PPCSTATE_OFF(pc)); + + MOVI2R(EncodeRegTo64(dest), (u64)asm_routines.dispatcher); + BR(EncodeRegTo64(dest)); +} + +void JitArm64::WriteExitDestInR(ARM64Reg Reg) +{ + STR(INDEX_UNSIGNED, Reg, X29, PPCSTATE_OFF(pc)); + gpr.Unlock(Reg); + DoDownCount(); + MOVI2R(EncodeRegTo64(Reg), (u64)asm_routines.dispatcher); + BR(EncodeRegTo64(Reg)); +} + +void STACKALIGN JitArm64::Run() +{ + CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode; + pExecAddr(); +} + +void JitArm64::SingleStep() +{ + CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode; + pExecAddr(); +} + +void STACKALIGN JitArm64::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* JitArm64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b) +{ + int blockSize = code_buf->GetSize(); + + if (Core::g_CoreStartupParameter.bEnableDebugging) + { + // Comment out the following to disable breakpoints (speed-up) + blockSize = 1; + } + + if (em_address == 0) + { + Core::SetState(Core::CORE_PAUSE); + WARN_LOG(DYNA_REC, "ERROR: Compiling at 0. LR=%08x CTR=%08x", LR, CTR); + } + + js.isLastInstruction = false; + js.blockStart = em_address; + js.fifoBytesThisBlock = 0; + js.downcountAmount = 0; + js.skipnext = false; + js.curBlock = b; + + u32 nextPC = em_address; + // 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. + nextPC = analyzer.Analyze(em_address, &code_block, code_buf, blockSize); + + PPCAnalyst::CodeOp *ops = code_buf->codebuffer; + + const u8 *start = GetCodePtr(); + b->checkedEntry = start; + b->runCount = 0; + + // Downcount flag check, Only valid for linked blocks + { + FixupBranch bail = B(CC_PL); + ARM64Reg WA = gpr.GetReg(); + ARM64Reg XA = EncodeRegTo64(WA); + MOVI2R(WA, js.blockStart); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc)); + MOVI2R(XA, (u64)asm_routines.doTiming); + BR(XA); + gpr.Unlock(WA); + SetJumpTarget(bail); + } + + const u8 *normalEntry = GetCodePtr(); + b->normalEntry = normalEntry; + + gpr.Start(js.gpa); + fpr.Start(js.fpa); + + if (!Core::g_CoreStartupParameter.bEnableDebugging) + js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address); + + // Translate instructions + for (u32 i = 0; i < code_block.m_num_instructions; i++) + { + js.compilerPC = ops[i].address; + js.op = &ops[i]; + js.instructionNumber = i; + const GekkoOPInfo *opinfo = ops[i].opinfo; + js.downcountAmount += opinfo->numCycles; + + if (i == (code_block.m_num_instructions - 1)) + { + // WARNING - cmp->branch merging will screw this up. + js.isLastInstruction = true; + js.next_inst = 0; + } + else + { + // help peephole optimizations + js.next_inst = ops[i + 1].inst; + js.next_compilerPC = ops[i + 1].address; + } + if (!ops[i].skip) + { + if (js.memcheck && (opinfo->flags & FL_USE_FPU)) + { + // Don't do this yet + BRK(0x7777); + } + + JitArm64Tables::CompileInstruction(ops[i]); + + if (js.memcheck && (opinfo->flags & FL_LOADSTORE)) + { + // Don't do this yet + BRK(0x666); + } + } + } + + if (code_block.m_memory_exception) + BRK(0x500); + + if (code_block.m_broken) + { + printf("Broken Block going to 0x%08x\n", nextPC); + WriteExit(nextPC); + } + + b->codeSize = (u32)(GetCodePtr() - normalEntry); + b->originalSize = code_block.m_num_instructions; + FlushIcache(); + return start; +} diff --git a/Source/Core/Core/PowerPC/JitArm64/Jit.h b/Source/Core/Core/PowerPC/JitArm64/Jit.h new file mode 100644 index 0000000000..a1ff3e525d --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/Jit.h @@ -0,0 +1,99 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Arm64Emitter.h" + +#include "Core/PowerPC/CPUCoreBase.h" +#include "Core/PowerPC/PPCAnalyst.h" +#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" +#include "Core/PowerPC/JitArm64/JitArm64Cache.h" +#include "Core/PowerPC/JitArm64/JitAsm.h" +#include "Core/PowerPC/JitCommon/JitBase.h" + +#define PPCSTATE_OFF(elem) ((s64)&PowerPC::ppcState.elem - (s64)&PowerPC::ppcState) +using namespace Arm64Gen; +class JitArm64 : public JitBase, public Arm64Gen::ARM64CodeBlock +{ +public: + JitArm64() : code_buffer(32000) {} + ~JitArm64() {} + + void Init(); + void Shutdown(); + + 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(); + + CommonAsmRoutinesBase *GetAsmRoutines() + { + return &asm_routines; + } + + void Run(); + void SingleStep(); + + void Jit(u32 em_address); + + const char *GetName() + { + return "JITARM64"; + } + + // OPCODES + void unknown_instruction(UGeckoInstruction inst); + void FallBackToInterpreter(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); + + // Force break + void Break(UGeckoInstruction inst); + + // Branch + void sc(UGeckoInstruction inst); + void rfi(UGeckoInstruction inst); + void bx(UGeckoInstruction inst); + void bcx(UGeckoInstruction inst); + void bcctrx(UGeckoInstruction inst); + void bclrx(UGeckoInstruction inst); + + // System Registers + void mtmsr(UGeckoInstruction inst); + + // LoadStore + void icbi(UGeckoInstruction inst); + +private: + Arm64GPRCache gpr; + Arm64FPRCache fpr; + + JitArm64BlockCache blocks; + JitArm64AsmRoutineManager asm_routines; + + PPCAnalyst::CodeBuffer code_buffer; + + const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b); + + void DoDownCount(); + + // Exits + void WriteExit(u32 destination); + void WriteExceptionExit(ARM64Reg dest); + void WriteExitDestInR(ARM64Reg dest); + + FixupBranch JumpIfCRFieldBit(int field, int bit, bool jump_if_set); +}; + diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp new file mode 100644 index 0000000000..55c319a7cc --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.cpp @@ -0,0 +1,16 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Core/PowerPC/JitInterface.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64Cache.h" + +void JitArm64BlockCache::WriteLinkBlock(u8* location, const u8* address) +{ +} + +void JitArm64BlockCache::WriteDestroyBlock(const u8* location, u32 address) +{ +} + diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h new file mode 100644 index 0000000000..3868751597 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64Cache.h @@ -0,0 +1,17 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Core/PowerPC/JitCommon/JitCache.h" + + +typedef void (*CompiledCode)(); + +class JitArm64BlockCache : public JitBaseBlockCache +{ +private: + void WriteLinkBlock(u8* location, const u8* address); + void WriteDestroyBlock(const u8* location, u32 address); +}; diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Branch.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Branch.cpp new file mode 100644 index 0000000000..037e995718 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Branch.cpp @@ -0,0 +1,254 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/PowerPC/PPCTables.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" +#include "Core/PowerPC/JitArm64/JitAsm.h" + +using namespace Arm64Gen; + +void JitArm64::sc(UGeckoInstruction inst) +{ + INSTRUCTION_START + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + ARM64Reg WA = gpr.GetReg(); + + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(Exceptions)); + ORR(WA, WA, 31, 0); // Same as WA | EXCEPTION_SYSCALL + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(Exceptions)); + + MOVI2R(WA, js.compilerPC + 4); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(pc)); + + // WA is unlocked in this function + WriteExceptionExit(WA); +} + +void JitArm64::rfi(UGeckoInstruction inst) +{ + INSTRUCTION_START + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + // See Interpreter rfi for details + 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 + ARM64Reg WA = gpr.GetReg(); + ARM64Reg WB = gpr.GetReg(); + ARM64Reg WC = gpr.GetReg(); + + MOVI2R(WA, (~mask) & clearMSR13); + MOVI2R(WB, mask & clearMSR13); + + LDR(INDEX_UNSIGNED, WC, X29, PPCSTATE_OFF(msr)); + + AND(WC, WC, WB, ArithOption(WC, ST_LSL, 0)); // rD = Masked MSR + + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_SRR1])); // rB contains SRR1 here + + AND(WA, WA, WB, ArithOption(WA, ST_LSL, 0)); // rB contains masked SRR1 here + ORR(WA, WA, WC, ArithOption(WA, ST_LSL, 0)); // rB = Masked MSR OR masked SRR1 + + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(msr)); // STR rB in to rA + + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_SRR0])); + gpr.Unlock(WB, WC); + + // WA is unlocked in this function + WriteExceptionExit(WA); +} + +void JitArm64::bx(UGeckoInstruction inst) +{ + INSTRUCTION_START + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + u32 destination; + if (inst.AA) + destination = SignExt26(inst.LI << 2); + else + destination = js.compilerPC + SignExt26(inst.LI << 2); + + if (inst.LK) + { + u32 Jumpto = js.compilerPC + 4; + ARM64Reg WA = gpr.GetReg(); + MOVI2R(WA, Jumpto); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR])); + gpr.Unlock(WA); + } + + if (destination == js.compilerPC) + { + // make idle loops go faster + ARM64Reg WA = gpr.GetReg(); + ARM64Reg XA = EncodeRegTo64(WA); + + MOVI2R(XA, (u64)&CoreTiming::Idle); + BLR(XA); + MOVI2R(WA, js.compilerPC); + WriteExceptionExit(WA); + } + + WriteExit(destination); +} + +void JitArm64::bcx(UGeckoInstruction inst) +{ + INSTRUCTION_START + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + ARM64Reg WA = gpr.GetReg(); + FixupBranch pCTRDontBranch; + if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR + { + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR])); + SUBS(WA, WA, 1); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR])); + + if (inst.BO & BO_BRANCH_IF_CTR_0) + pCTRDontBranch = B(CC_NEQ); + else + pCTRDontBranch = B(CC_EQ); + } + + FixupBranch pConditionDontBranch; + + if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit + { + pConditionDontBranch = JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), + !(inst.BO_2 & BO_BRANCH_IF_TRUE)); + } + + if (inst.LK) + { + u32 Jumpto = js.compilerPC + 4; + MOVI2R(WA, Jumpto); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR])); + } + gpr.Unlock(WA); + + u32 destination; + if (inst.AA) + destination = SignExt16(inst.BD << 2); + else + destination = js.compilerPC + SignExt16(inst.BD << 2); + WriteExit(destination); + + if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) + SetJumpTarget( pConditionDontBranch ); + if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + SetJumpTarget( pCTRDontBranch ); + + WriteExit(js.compilerPC + 4); +} + +void JitArm64::bcctrx(UGeckoInstruction inst) +{ + INSTRUCTION_START + + // bcctrx doesn't decrement and/or test CTR + _assert_msg_(DYNA_REC, inst.BO_2 & BO_DONT_DECREMENT_FLAG, "bcctrx with decrement and test CTR option is invalid!"); + + if (inst.BO_2 & BO_DONT_CHECK_CONDITION) + { + // BO_2 == 1z1zz -> b always + + //NPC = CTR & 0xfffffffc; + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + if (inst.LK_3) + { + ARM64Reg WB = gpr.GetReg(); + u32 Jumpto = js.compilerPC + 4; + MOVI2R(WB, Jumpto); + STR(INDEX_UNSIGNED, WB, X29, PPCSTATE_OFF(spr[SPR_LR])); + gpr.Unlock(WB); + } + + ARM64Reg WA = gpr.GetReg(); + + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR])); + AND(WA, WA, 30, 29); // Wipe the bottom 2 bits. + WriteExitDestInR(WA); + } + else + { + // Rare condition seen in (just some versions of?) Nintendo's NES Emulator + // BO_2 == 001zy -> b if false + // BO_2 == 011zy -> b if true + _assert_msg_(DYNA_REC, false, "Haven't implemented rare form of bcctrx yet"); + } +} + +void JitArm64::bclrx(UGeckoInstruction inst) +{ + INSTRUCTION_START + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + ARM64Reg WA = gpr.GetReg(); + FixupBranch pCTRDontBranch; + if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) // Decrement and test CTR + { + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR])); + SUBS(WA, WA, 1); + STR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_CTR])); + + if (inst.BO & BO_BRANCH_IF_CTR_0) + pCTRDontBranch = B(CC_NEQ); + else + pCTRDontBranch = B(CC_EQ); + } + + FixupBranch pConditionDontBranch; + if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) // Test a CR bit + { + pConditionDontBranch = JumpIfCRFieldBit(inst.BI >> 2, 3 - (inst.BI & 3), + !(inst.BO_2 & BO_BRANCH_IF_TRUE)); + } + + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(spr[SPR_LR])); + AND(WA, WA, 30, 29); // Wipe the bottom 2 bits. + + if (inst.LK) + { + ARM64Reg WB = gpr.GetReg(); + u32 Jumpto = js.compilerPC + 4; + MOVI2R(WB, Jumpto); + STR(INDEX_UNSIGNED, WB, X29, PPCSTATE_OFF(spr[SPR_LR])); + gpr.Unlock(WB); + } + + WriteExitDestInR(WA); + + if ((inst.BO & BO_DONT_CHECK_CONDITION) == 0) + SetJumpTarget( pConditionDontBranch ); + if ((inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + SetJumpTarget( pCTRDontBranch ); + + WriteExit(js.compilerPC + 4); +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp new file mode 100644 index 0000000000..783a9ce78b --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_LoadStore.cpp @@ -0,0 +1,22 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/PowerPC/PPCTables.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" +#include "Core/PowerPC/JitArm64/JitAsm.h" + +using namespace Arm64Gen; + +void JitArm64::icbi(UGeckoInstruction inst) +{ + FallBackToInterpreter(inst); + WriteExit(js.compilerPC + 4); +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp new file mode 100644 index 0000000000..a165828037 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.cpp @@ -0,0 +1,319 @@ +// copyright 2014 dolphin emulator project +// licensed under gplv2 +// refer to the license.txt file included. + +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_RegCache.h" + +using namespace Arm64Gen; + +void Arm64RegCache::Init(ARM64XEmitter *emitter) +{ + m_emit = emitter; + GetAllocationOrder(); +} + +ARM64Reg Arm64RegCache::GetReg(void) +{ + for (auto& it : m_host_registers) + { + if (!it.IsLocked()) + { + it.Lock(); + return it.GetReg(); + } + } + // Holy cow, how did you run out of registers? + // We can't return anything reasonable in this case. Return INVALID_REG and watch the failure happen + _assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb"); + return INVALID_REG; +} + +void Arm64RegCache::LockRegister(ARM64Reg host_reg) +{ + auto reg = std::find(m_host_registers.begin(), m_host_registers.end(), host_reg); + if (reg == m_host_registers.end()) + _assert_msg_(DYNA_REC, false, "Don't try locking a register that isn't in the cache"); + _assert_msg_(DYNA_REC, !reg->IsLocked(), "This register is already locked"); + reg->Lock(); +} + +void Arm64RegCache::UnlockRegister(ARM64Reg host_reg) +{ + auto reg = std::find(m_host_registers.begin(), m_host_registers.end(), host_reg); + if (reg == m_host_registers.end()) + _assert_msg_(DYNA_REC, false, "Don't try unlocking a register that isn't in the cache"); + _assert_msg_(DYNA_REC, reg->IsLocked(), "This register is already unlocked"); + reg->Unlock(); +} + +// GPR Cache +void Arm64GPRCache::Start(PPCAnalyst::BlockRegStats &stats) +{ + // To make this technique easy, let's just work on pairs of even/odd registers + // We could do simple odd/even as well to get a few spare temporary registers + // but it isn't really needed, we aren't starved for registers + for (int reg = 0; reg < 32; reg += 2) + { + u32 regs_used = (stats.IsUsed(reg) << 1) | stats.IsUsed(reg + 1); + switch (regs_used) + { + case 0x02: // Reg+0 used + { + ARM64Reg host_reg = GetReg(); + m_guest_registers[reg].LoadToReg(host_reg); + m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg])); + } + break; + case 0x01: // Reg+1 used + { + ARM64Reg host_reg = GetReg(); + m_guest_registers[reg + 1].LoadToReg(host_reg); + m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg + 1])); + } + break; + case 0x03: // Both registers used + { + // Get a 64bit host register + ARM64Reg host_reg = EncodeRegTo64(GetReg()); + m_guest_registers[reg].LoadToAway(host_reg, REG_LOW); + m_guest_registers[reg + 1].LoadToAway(host_reg, REG_HIGH); + + // host_reg is 64bit here. + // It'll load both guest_registers in one LDR + m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[reg])); + } + break; + case 0x00: // Neither used + default: + break; + } + } +} + +bool Arm64GPRCache::IsCalleeSaved(ARM64Reg reg) +{ + static std::vector callee_regs = + { + X28, X27, X26, X25, X24, X23, X22, X21, X20, + X19, INVALID_REG, + }; + return std::find(callee_regs.begin(), callee_regs.end(), EncodeRegTo64(reg)) != callee_regs.end(); +} + +void Arm64GPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op) +{ + for (int i = 0; i < 32; ++i) + { + bool flush = true; + if (mode == FLUSH_INTERPRETER) + { + if (!(op->regsOut[0] == i || + op->regsOut[1] == i || + op->regsIn[0] == i || + op->regsIn[1] == i || + op->regsIn[2] == i)) + { + // This interpreted instruction doesn't use this register + flush = false; + } + } + + if (m_guest_registers[i].GetType() == REG_REG) + { + // Has to be flushed if it isn't in a callee saved register + ARM64Reg host_reg = m_guest_registers[i].GetReg(); + if (flush || !IsCalleeSaved(host_reg)) + { + m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i])); + Unlock(host_reg); + + m_guest_registers[i].Flush(); + } + } + else if (m_guest_registers[i].GetType() == REG_IMM) + { + if (flush) + { + ARM64Reg host_reg = GetReg(); + + m_emit->MOVI2R(host_reg, m_guest_registers[i].GetImm()); + m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i])); + + Unlock(host_reg); + + m_guest_registers[i].Flush(); + } + } + else if (m_guest_registers[i].GetType() == REG_AWAY) + { + // We are away, that means that this register and the next are stored in a single 64bit register + // There is a very good chance that both the registers are out in some "temp" register + bool flush_2 = true; + if (mode == FLUSH_INTERPRETER) + { + if (!(op->regsOut[0] == (i + 1) || + op->regsOut[1] == (i + 1) || + op->regsIn[0] == (i + 1) || + op->regsIn[1] == (i + 1) || + op->regsIn[2] == (i + 1))) + { + // This interpreted instruction doesn't use this register + flush_2 = false; + } + } + + ARM64Reg host_reg = m_guest_registers[i].GetAwayReg(); + ARM64Reg host_reg_1 = m_guest_registers[i].GetReg(); + ARM64Reg host_reg_2 = m_guest_registers[i + 1].GetReg(); + // Flush if either of these shared registers are used. + if (flush || + flush_2 || + !IsCalleeSaved(host_reg) || + !IsCalleeSaved(host_reg_1) || + !IsCalleeSaved(host_reg_2)) + { + + if (host_reg_1 == INVALID_REG) + { + // We never loaded this register + // We've got to test the state of our shared register + // Currently it is always reg+1 + if (host_reg_2 == INVALID_REG) + { + // We didn't load either of these registers + // This can happen in cases where we had to flush register state + // or if we hit an interpreted instruction before we could use it + // Dump the whole thing in one go and flush both registers + + // 64bit host register will store 2 32bit store registers in one go + m_emit->STR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[i])); + } + else + { + // Alright, bottom register isn't used, but top one is + // Only store the top one + m_emit->STR(INDEX_UNSIGNED, host_reg_2, X29, PPCSTATE_OFF(gpr[i + 1])); + Unlock(host_reg_2); + } + } + else + { + m_emit->STR(INDEX_UNSIGNED, host_reg_1, X29, PPCSTATE_OFF(gpr[i])); + Unlock(host_reg_1); + } + // Flush both registers + m_guest_registers[i].Flush(); + m_guest_registers[i + 1].Flush(); + Unlock(DecodeReg(host_reg)); + } + // Skip the next register since we've handled it here + ++i; + } + } +} + +ARM64Reg Arm64GPRCache::R(u32 preg) +{ + OpArg& reg = m_guest_registers[preg]; + switch (reg.GetType()) + { + case REG_REG: // already in a reg + return reg.GetReg(); + break; + case REG_IMM: // Is an immediate + { + ARM64Reg host_reg = GetReg(); + m_emit->MOVI2R(host_reg, reg.GetImm()); + } + break; + case REG_AWAY: // Register is away in a shared register + { + // Let's do the voodoo that we dodo + if (reg.GetReg() == INVALID_REG) + { + // Alright, we need to move to a valid location + ARM64Reg host_reg = GetReg(); + reg.LoadAwayToReg(host_reg); + + // Alright, we need to extract from our away register + // To our new 32bit register + if (reg.GetAwayLocation() == REG_LOW) + { + // We are in the low bits + // Just move it over to the low bits of the new register + m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 0, 31); + } + else + { + // We are in the high bits + m_emit->UBFM(EncodeRegTo64(host_reg), reg.GetAwayReg(), 32, 63); + } + } + else + { + // We've already moved to a valid place to work on + return reg.GetReg(); + } + } + break; + case REG_NOTLOADED: // Register isn't loaded at /all/ + { + // This is kind of annoying, we shouldn't have gotten here + // This can happen with instructions that use multiple registers(eg lmw) + // The PPCAnalyst needs to be modified to handle these cases + _dbg_assert_msg_(DYNA_REC, false, "Hit REG_NOTLOADED type oparg. Fix the PPCAnalyst"); + ARM64Reg host_reg = GetReg(); + reg.LoadToReg(host_reg); + m_emit->LDR(INDEX_UNSIGNED, host_reg, X29, PPCSTATE_OFF(gpr[preg])); + return host_reg; + } + break; + default: + _dbg_assert_msg_(DYNA_REC, false, "Invalid OpArg Type!"); + break; + } + // We've got an issue if we end up here + return INVALID_REG; +} + +void Arm64GPRCache::GetAllocationOrder(void) +{ + // Callee saved registers first in hopes that we will keep everything stored there first + const std::vector allocation_order = + { + W28, W27, W26, W25, W24, W23, W22, W21, W20, + W19, W0, W1, W2, W3, W4, W5, W6, W7, W8, W9, + W10, W11, W12, W13, W14, W15, W16, W17, W18, + W30, + }; + + for (ARM64Reg reg : allocation_order) + m_host_registers.push_back(HostReg(reg)); +} + +// FPR Cache +void Arm64FPRCache::Flush(FlushMode mode, PPCAnalyst::CodeOp* op) +{ + // XXX: Flush our stuff +} + +ARM64Reg Arm64FPRCache::R(u32 preg) +{ + // XXX: return a host reg holding a guest register +} + +void Arm64FPRCache::GetAllocationOrder(void) +{ + const std::vector allocation_order = + { + D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, + D11, D12, D13, D14, D15, D16, D17, D18, D19, + D20, D21, D22, D23, D24, D25, D26, D27, D28, + D29, D30, D31, + }; + + for (ARM64Reg reg : allocation_order) + m_host_registers.push_back(HostReg(reg)); +} + diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h new file mode 100644 index 0000000000..26dd213f56 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_RegCache.h @@ -0,0 +1,242 @@ +// copyright 2014 dolphin emulator project +// licensed under gplv2 +// refer to the license.txt file included. + +#pragma once + +#include + +#include "Common/Arm64Emitter.h" +#include "Core/PowerPC/Gekko.h" +#include "Core/PowerPC/PPCAnalyst.h" + +// Dedicated host registers +// X29 = ppcState pointer +using namespace Arm64Gen; + +enum RegType +{ + REG_NOTLOADED = 0, + REG_REG, // Reg type is register + REG_IMM, // Reg is really a IMM + REG_AWAY, // Reg is away +}; +enum RegLocation +{ + REG_LOW = 0, + REG_HIGH, +}; + +enum FlushMode +{ + // Flushes all registers, no exceptions + FLUSH_ALL = 0, + // Flushes registers in a conditional branch + // Doesn't wipe the state of the registers from the cache + FLUSH_MAINTAIN_STATE, + // Flushes only the required registers for an interpreter call + FLUSH_INTERPRETER, +}; + +class OpArg +{ +public: + OpArg() + { + m_type = REG_NOTLOADED; + m_reg = INVALID_REG; + m_value = 0; + } + + RegType GetType() + { + return m_type; + } + + ARM64Reg GetReg() + { + return m_reg; + } + ARM64Reg GetAwayReg() + { + return m_away_reg; + } + RegLocation GetAwayLocation() + { + return m_away_location; + } + u32 GetImm() + { + return m_value; + } + void LoadToReg(ARM64Reg reg) + { + m_type = REG_REG; + m_reg = reg; + } + void LoadToAway(ARM64Reg reg, RegLocation location) + { + m_type = REG_AWAY; + m_reg = INVALID_REG; + m_away_reg = reg; + m_away_location = location; + } + void LoadAwayToReg(ARM64Reg reg) + { + // We are still an away type + // We just are also in another register + m_reg = reg; + } + void LoadToImm(u32 imm) + { + m_type = REG_IMM; + m_value = imm; + } + void Flush() + { + m_type = REG_NOTLOADED; + } + +private: + // For REG_REG + RegType m_type; // store type + ARM64Reg m_reg; // host register we are in + + // For REG_AWAY + // Host register that we are away in + // This is a 64bit register + ARM64Reg m_away_reg; + RegLocation m_away_location; + + // For REG_IMM + u32 m_value; // IMM value +}; + +class HostReg +{ +public: + HostReg() : m_reg(INVALID_REG), m_locked(false) {} + HostReg(ARM64Reg reg) : m_reg(reg), m_locked(false) {} + bool IsLocked(void) { return m_locked; } + void Lock(void) { m_locked = true; } + void Unlock(void) { m_locked = false; } + ARM64Reg GetReg(void) { return m_reg; } + + bool operator==(const ARM64Reg& reg) + { + return reg == m_reg; + } + +private: + ARM64Reg m_reg; + bool m_locked; +}; + +class Arm64RegCache +{ +public: + Arm64RegCache(void) : m_emit(nullptr), m_reg_stats(nullptr) {}; + virtual ~Arm64RegCache() {}; + + void Init(ARM64XEmitter *emitter); + + virtual void Start(PPCAnalyst::BlockRegStats &stats) {} + + // Flushes the register cache in different ways depending on the mode + virtual void Flush(FlushMode mode, PPCAnalyst::CodeOp* op) = 0; + + // Returns a guest register inside of a host register + // Will dump an immediate to the host register as well + virtual ARM64Reg R(u32 reg) = 0; + + // Returns a temporary register for use + // Requires unlocking after done + ARM64Reg GetReg(void); + + // Locks a register so a cache cannot use it + // Useful for function calls + template + void Lock(Args... args) + { + for (T reg : {args...}) + { + LockRegister(reg); + } + } + + // Unlocks a locked register + // Unlocks registers locked with both GetReg and LockRegister + template + void Unlock(Args... args) + { + for (T reg : {args...}) + { + UnlockRegister(reg); + } + } + +protected: + // Get the order of the host registers + virtual void GetAllocationOrder(void) = 0; + + // Lock a register + void LockRegister(ARM64Reg host_reg); + + // Unlock a register + void UnlockRegister(ARM64Reg host_reg); + + // Code emitter + ARM64XEmitter *m_emit; + + // Host side registers that hold the host registers in order of use + std::vector m_host_registers; + + // Register stats for the current block + PPCAnalyst::BlockRegStats *m_reg_stats; +}; + +class Arm64GPRCache : public Arm64RegCache +{ +public: + ~Arm64GPRCache() {} + + void Start(PPCAnalyst::BlockRegStats &stats); + + // Flushes the register cache in different ways depending on the mode + void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr); + + // Returns a guest register inside of a host register + // Will dump an immediate to the host register as well + ARM64Reg R(u32 preg); + +protected: + // Get the order of the host registers + void GetAllocationOrder(void); + + // Our guest GPRs + // PowerPC has 32 GPRs + OpArg m_guest_registers[32]; + +private: + bool IsCalleeSaved(ARM64Reg reg); +}; + +class Arm64FPRCache : public Arm64RegCache +{ +public: + ~Arm64FPRCache() {} + // Flushes the register cache in different ways depending on the mode + void Flush(FlushMode mode, PPCAnalyst::CodeOp* op = nullptr); + + // Returns a guest register inside of a host register + // Will dump an immediate to the host register as well + ARM64Reg R(u32 preg); + +protected: + // Get the order of the host registers + void GetAllocationOrder(void); + + // Our guest FPRs + // Gekko has 32 paired registers(32x2) + OpArg m_guest_registers[32][2]; +}; diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp new file mode 100644 index 0000000000..7c72a0ccac --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_SystemRegisters.cpp @@ -0,0 +1,60 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" +#include "Common/Common.h" + +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/PowerPC/PPCTables.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitAsm.h" + +FixupBranch JitArm64::JumpIfCRFieldBit(int field, int bit, bool jump_if_set) +{ + ARM64Reg WA = gpr.GetReg(); + ARM64Reg XA = EncodeRegTo64(WA); + + FixupBranch branch; + switch (bit) + { + case CR_SO_BIT: // check bit 61 set + LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field])); + branch = jump_if_set ? TBNZ(XA, 61) : TBZ(XA, 61); + break; + case CR_EQ_BIT: // check bits 31-0 == 0 + LDR(INDEX_UNSIGNED, WA, X29, PPCSTATE_OFF(cr_val[field])); + branch = jump_if_set ? CBZ(WA) : CBNZ(WA); + break; + case CR_GT_BIT: // check val > 0 + LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field])); + CMP(XA, SP); + branch = B(jump_if_set ? CC_GT : CC_LE); + break; + case CR_LT_BIT: // check bit 62 set + LDR(INDEX_UNSIGNED, XA, X29, PPCSTATE_OFF(cr_val[field])); + branch = jump_if_set ? TBNZ(XA, 62) : TBZ(XA, 62); + break; + default: + _assert_msg_(DYNA_REC, false, "Invalid CR bit"); + } + + gpr.Unlock(WA); + return branch; +} + +void JitArm64::mtmsr(UGeckoInstruction inst) +{ + INSTRUCTION_START + // Don't interpret this, if we do we get thrown out + //JITDISABLE(bJITSystemRegistersOff) + + STR(INDEX_UNSIGNED, gpr.R(inst.RS), X29, PPCSTATE_OFF(msr)); + + gpr.Flush(FlushMode::FLUSH_ALL); + fpr.Flush(FlushMode::FLUSH_ALL); + + WriteExit(js.compilerPC + 4); +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.cpp b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.cpp new file mode 100644 index 0000000000..d798ed5056 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.cpp @@ -0,0 +1,493 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Core/PowerPC/JitInterface.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_Tables.h" + +// Should be moved in to the Jit class +typedef void (JitArm64::*_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 JitArm64::DynaRunTable4(UGeckoInstruction inst) {(this->*dynaOpTable4 [inst.SUBOP10])(inst);} +void JitArm64::DynaRunTable19(UGeckoInstruction inst) {(this->*dynaOpTable19[inst.SUBOP10])(inst);} +void JitArm64::DynaRunTable31(UGeckoInstruction inst) {(this->*dynaOpTable31[inst.SUBOP10])(inst);} +void JitArm64::DynaRunTable59(UGeckoInstruction inst) {(this->*dynaOpTable59[inst.SUBOP5 ])(inst);} +void JitArm64::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, &JitArm64::DynaRunTable4}, //"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}}, + {19, &JitArm64::DynaRunTable19}, //"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}}, + {31, &JitArm64::DynaRunTable31}, //"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}}, + {59, &JitArm64::DynaRunTable59}, //"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}}, + {63, &JitArm64::DynaRunTable63}, //"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}}, + + {16, &JitArm64::bcx}, //"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {18, &JitArm64::bx}, //"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + + {1, &JitArm64::HLEFunction}, //"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {2, &JitArm64::FallBackToInterpreter}, //"DynaBlock", OPTYPE_SYSTEM, 0}}, + {3, &JitArm64::Break}, //"twi", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {17, &JitArm64::sc}, //"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, + + {7, &JitArm64::FallBackToInterpreter}, //"mulli", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_RC_BIT, 2}}, + {8, &JitArm64::FallBackToInterpreter}, //"subfic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}}, + {10, &JitArm64::FallBackToInterpreter}, //"cmpli", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}}, + {11, &JitArm64::FallBackToInterpreter}, //"cmpi", OPTYPE_INTEGER, FL_IN_A | FL_SET_CRn}}, + {12, &JitArm64::FallBackToInterpreter}, //"addic", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CA}}, + {13, &JitArm64::FallBackToInterpreter}, //"addic_rc", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_SET_CR0}}, + {14, &JitArm64::FallBackToInterpreter}, //"addi", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}}, + {15, &JitArm64::FallBackToInterpreter}, //"addis", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A0}}, + + {20, &JitArm64::FallBackToInterpreter}, //"rlwimix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_A | FL_IN_S | FL_RC_BIT}}, + {21, &JitArm64::FallBackToInterpreter}, //"rlwinmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {23, &JitArm64::FallBackToInterpreter}, //"rlwnmx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_IN_B | FL_RC_BIT}}, + + {24, &JitArm64::FallBackToInterpreter}, //"ori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {25, &JitArm64::FallBackToInterpreter}, //"oris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {26, &JitArm64::FallBackToInterpreter}, //"xori", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {27, &JitArm64::FallBackToInterpreter}, //"xoris", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S}}, + {28, &JitArm64::FallBackToInterpreter}, //"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}}, + {29, &JitArm64::FallBackToInterpreter}, //"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}}, + + {32, &JitArm64::FallBackToInterpreter}, //"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {33, &JitArm64::FallBackToInterpreter}, //"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {34, &JitArm64::FallBackToInterpreter}, //"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {35, &JitArm64::FallBackToInterpreter}, //"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {40, &JitArm64::FallBackToInterpreter}, //"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {41, &JitArm64::FallBackToInterpreter}, //"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + {42, &JitArm64::FallBackToInterpreter}, //"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}}, + {43, &JitArm64::FallBackToInterpreter}, //"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}}, + + {44, &JitArm64::FallBackToInterpreter}, //"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {45, &JitArm64::FallBackToInterpreter}, //"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + {36, &JitArm64::FallBackToInterpreter}, //"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {37, &JitArm64::FallBackToInterpreter}, //"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + {38, &JitArm64::FallBackToInterpreter}, //"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}}, + {39, &JitArm64::FallBackToInterpreter}, //"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}}, + + {46, &JitArm64::FallBackToInterpreter}, //"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}}, + {47, &JitArm64::FallBackToInterpreter}, //"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}}, + + {48, &JitArm64::FallBackToInterpreter}, //"lfs", OPTYPE_LOADFP, FL_IN_A}}, + {49, &JitArm64::FallBackToInterpreter}, //"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}}, + {50, &JitArm64::FallBackToInterpreter}, //"lfd", OPTYPE_LOADFP, FL_IN_A}}, + {51, &JitArm64::FallBackToInterpreter}, //"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A}}, + + {52, &JitArm64::FallBackToInterpreter}, //"stfs", OPTYPE_STOREFP, FL_IN_A}}, + {53, &JitArm64::FallBackToInterpreter}, //"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}}, + {54, &JitArm64::FallBackToInterpreter}, //"stfd", OPTYPE_STOREFP, FL_IN_A}}, + {55, &JitArm64::FallBackToInterpreter}, //"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A}}, + + {56, &JitArm64::FallBackToInterpreter}, //"psq_l", OPTYPE_PS, FL_IN_A}}, + {57, &JitArm64::FallBackToInterpreter}, //"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A}}, + {60, &JitArm64::FallBackToInterpreter}, //"psq_st", OPTYPE_PS, FL_IN_A}}, + {61, &JitArm64::FallBackToInterpreter}, //"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A}}, + + //missing: 0, 5, 6, 9, 22, 30, 62, 58 + {0, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {5, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {6, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {9, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {22, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {30, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {62, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {58, &JitArm64::FallBackToInterpreter}, //"unknown_instruction", OPTYPE_UNKNOWN, 0}}, +}; + +static GekkoOPTemplate table4[] = +{ //SUBOP10 + {0, &JitArm64::FallBackToInterpreter}, //"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}}, + {32, &JitArm64::FallBackToInterpreter}, //"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}}, + {40, &JitArm64::FallBackToInterpreter}, //"ps_neg", OPTYPE_PS, FL_RC_BIT}}, + {136, &JitArm64::FallBackToInterpreter}, //"ps_nabs", OPTYPE_PS, FL_RC_BIT}}, + {264, &JitArm64::FallBackToInterpreter}, //"ps_abs", OPTYPE_PS, FL_RC_BIT}}, + {64, &JitArm64::FallBackToInterpreter}, //"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}}, + {72, &JitArm64::FallBackToInterpreter}, //"ps_mr", OPTYPE_PS, FL_RC_BIT}}, + {96, &JitArm64::FallBackToInterpreter}, //"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}}, + {528, &JitArm64::FallBackToInterpreter}, //"ps_merge00", OPTYPE_PS, FL_RC_BIT}}, + {560, &JitArm64::FallBackToInterpreter}, //"ps_merge01", OPTYPE_PS, FL_RC_BIT}}, + {592, &JitArm64::FallBackToInterpreter}, //"ps_merge10", OPTYPE_PS, FL_RC_BIT}}, + {624, &JitArm64::FallBackToInterpreter}, //"ps_merge11", OPTYPE_PS, FL_RC_BIT}}, + + {1014, &JitArm64::FallBackToInterpreter}, //"dcbz_l", OPTYPE_SYSTEM, 0}}, +}; + +static GekkoOPTemplate table4_2[] = +{ + {10, &JitArm64::FallBackToInterpreter}, //"ps_sum0", OPTYPE_PS, 0}}, + {11, &JitArm64::FallBackToInterpreter}, //"ps_sum1", OPTYPE_PS, 0}}, + {12, &JitArm64::FallBackToInterpreter}, //"ps_muls0", OPTYPE_PS, 0}}, + {13, &JitArm64::FallBackToInterpreter}, //"ps_muls1", OPTYPE_PS, 0}}, + {14, &JitArm64::FallBackToInterpreter}, //"ps_madds0", OPTYPE_PS, 0}}, + {15, &JitArm64::FallBackToInterpreter}, //"ps_madds1", OPTYPE_PS, 0}}, + {18, &JitArm64::FallBackToInterpreter}, //"ps_div", OPTYPE_PS, 0, 16}}, + {20, &JitArm64::FallBackToInterpreter}, //"ps_sub", OPTYPE_PS, 0}}, + {21, &JitArm64::FallBackToInterpreter}, //"ps_add", OPTYPE_PS, 0}}, + {23, &JitArm64::FallBackToInterpreter}, //"ps_sel", OPTYPE_PS, 0}}, + {24, &JitArm64::FallBackToInterpreter}, //"ps_res", OPTYPE_PS, 0}}, + {25, &JitArm64::FallBackToInterpreter}, //"ps_mul", OPTYPE_PS, 0}}, + {26, &JitArm64::FallBackToInterpreter}, //"ps_rsqrte", OPTYPE_PS, 0, 1}}, + {28, &JitArm64::FallBackToInterpreter}, //"ps_msub", OPTYPE_PS, 0}}, + {29, &JitArm64::FallBackToInterpreter}, //"ps_madd", OPTYPE_PS, 0}}, + {30, &JitArm64::FallBackToInterpreter}, //"ps_nmsub", OPTYPE_PS, 0}}, + {31, &JitArm64::FallBackToInterpreter}, //"ps_nmadd", OPTYPE_PS, 0}}, +}; + + +static GekkoOPTemplate table4_3[] = +{ + {6, &JitArm64::FallBackToInterpreter}, //"psq_lx", OPTYPE_PS, 0}}, + {7, &JitArm64::FallBackToInterpreter}, //"psq_stx", OPTYPE_PS, 0}}, + {38, &JitArm64::FallBackToInterpreter}, //"psq_lux", OPTYPE_PS, 0}}, + {39, &JitArm64::FallBackToInterpreter}, //"psq_stux", OPTYPE_PS, 0}}, +}; + +static GekkoOPTemplate table19[] = +{ + {528, &JitArm64::bcctrx}, //"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {16, &JitArm64::bclrx}, //"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {257, &JitArm64::FallBackToInterpreter}, //"crand", OPTYPE_CR, FL_EVIL}}, + {129, &JitArm64::FallBackToInterpreter}, //"crandc", OPTYPE_CR, FL_EVIL}}, + {289, &JitArm64::FallBackToInterpreter}, //"creqv", OPTYPE_CR, FL_EVIL}}, + {225, &JitArm64::FallBackToInterpreter}, //"crnand", OPTYPE_CR, FL_EVIL}}, + {33, &JitArm64::FallBackToInterpreter}, //"crnor", OPTYPE_CR, FL_EVIL}}, + {449, &JitArm64::FallBackToInterpreter}, //"cror", OPTYPE_CR, FL_EVIL}}, + {417, &JitArm64::FallBackToInterpreter}, //"crorc", OPTYPE_CR, FL_EVIL}}, + {193, &JitArm64::FallBackToInterpreter}, //"crxor", OPTYPE_CR, FL_EVIL}}, + + {150, &JitArm64::FallBackToInterpreter}, //"isync", OPTYPE_ICACHE, FL_EVIL}}, + {0, &JitArm64::FallBackToInterpreter}, //"mcrf", OPTYPE_SYSTEM, FL_EVIL}}, + + {50, &JitArm64::rfi}, //"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}}, + {18, &JitArm64::Break}, //"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}} +}; + + +static GekkoOPTemplate table31[] = +{ + {28, &JitArm64::FallBackToInterpreter}, //"andx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {60, &JitArm64::FallBackToInterpreter}, //"andcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {444, &JitArm64::FallBackToInterpreter}, //"orx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {124, &JitArm64::FallBackToInterpreter}, //"norx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {316, &JitArm64::FallBackToInterpreter}, //"xorx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {412, &JitArm64::FallBackToInterpreter}, //"orcx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {476, &JitArm64::FallBackToInterpreter}, //"nandx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {284, &JitArm64::FallBackToInterpreter}, //"eqvx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_SB | FL_RC_BIT}}, + {0, &JitArm64::FallBackToInterpreter}, //"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {32, &JitArm64::FallBackToInterpreter}, //"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {26, &JitArm64::FallBackToInterpreter}, //"cntlzwx",OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {922, &JitArm64::FallBackToInterpreter}, //"extshx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {954, &JitArm64::FallBackToInterpreter}, //"extsbx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_RC_BIT}}, + {536, &JitArm64::FallBackToInterpreter}, //"srwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {792, &JitArm64::FallBackToInterpreter}, //"srawx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {824, &JitArm64::FallBackToInterpreter}, //"srawix", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + {24, &JitArm64::FallBackToInterpreter}, //"slwx", OPTYPE_INTEGER, FL_OUT_A | FL_IN_B | FL_IN_S | FL_RC_BIT}}, + + {54, &JitArm64::FallBackToInterpreter}, //"dcbst", OPTYPE_DCACHE, 0, 4}}, + {86, &JitArm64::FallBackToInterpreter}, //"dcbf", OPTYPE_DCACHE, 0, 4}}, + {246, &JitArm64::FallBackToInterpreter}, //"dcbtst", OPTYPE_DCACHE, 0, 1}}, + {278, &JitArm64::FallBackToInterpreter}, //"dcbt", OPTYPE_DCACHE, 0, 1}}, + {470, &JitArm64::FallBackToInterpreter}, //"dcbi", OPTYPE_DCACHE, 0, 4}}, + {758, &JitArm64::FallBackToInterpreter}, //"dcba", OPTYPE_DCACHE, 0, 4}}, + {1014, &JitArm64::FallBackToInterpreter}, //"dcbz", OPTYPE_DCACHE, 0, 4}}, + + //load word + {23, &JitArm64::FallBackToInterpreter}, //"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {55, &JitArm64::FallBackToInterpreter}, //"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load halfword + {279, &JitArm64::FallBackToInterpreter}, //"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {311, &JitArm64::FallBackToInterpreter}, //"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load halfword signextend + {343, &JitArm64::FallBackToInterpreter}, //"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {375, &JitArm64::FallBackToInterpreter}, //"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load byte + {87, &JitArm64::FallBackToInterpreter}, //"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {119, &JitArm64::FallBackToInterpreter}, //"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //load byte reverse + {534, &JitArm64::FallBackToInterpreter}, //"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {790, &JitArm64::FallBackToInterpreter}, //"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + + // Conditional load/store (Wii SMP) + {150, &JitArm64::FallBackToInterpreter}, //"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}}, + {20, &JitArm64::FallBackToInterpreter}, //"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}}, + + //load string (interpret these) + {533, &JitArm64::FallBackToInterpreter}, //"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}}, + {597, &JitArm64::FallBackToInterpreter}, //"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}}, + + //store word + {151, &JitArm64::FallBackToInterpreter}, //"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {183, &JitArm64::FallBackToInterpreter}, //"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store halfword + {407, &JitArm64::FallBackToInterpreter}, //"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {439, &JitArm64::FallBackToInterpreter}, //"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store byte + {215, &JitArm64::FallBackToInterpreter}, //"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {247, &JitArm64::FallBackToInterpreter}, //"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}}, + + //store bytereverse + {662, &JitArm64::FallBackToInterpreter}, //"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {918, &JitArm64::FallBackToInterpreter}, //"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + {661, &JitArm64::FallBackToInterpreter}, //"stswx", OPTYPE_STORE, FL_EVIL}}, + {725, &JitArm64::FallBackToInterpreter}, //"stswi", OPTYPE_STORE, FL_EVIL}}, + + // fp load/store + {535, &JitArm64::FallBackToInterpreter}, //"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {567, &JitArm64::FallBackToInterpreter}, //"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + {599, &JitArm64::FallBackToInterpreter}, //"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {631, &JitArm64::FallBackToInterpreter}, //"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + + {663, &JitArm64::FallBackToInterpreter}, //"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {695, &JitArm64::FallBackToInterpreter}, //"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {727, &JitArm64::FallBackToInterpreter}, //"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {759, &JitArm64::FallBackToInterpreter}, //"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {983, &JitArm64::FallBackToInterpreter}, //"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + + {19, &JitArm64::FallBackToInterpreter}, //"mfcr", OPTYPE_SYSTEM, FL_OUT_D}}, + {83, &JitArm64::FallBackToInterpreter}, //"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}}, + {144, &JitArm64::FallBackToInterpreter}, //"mtcrf", OPTYPE_SYSTEM, 0}}, + {146, &JitArm64::mtmsr}, //"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {210, &JitArm64::FallBackToInterpreter}, //"mtsr", OPTYPE_SYSTEM, 0}}, + {242, &JitArm64::FallBackToInterpreter}, //"mtsrin", OPTYPE_SYSTEM, 0}}, + {339, &JitArm64::FallBackToInterpreter}, //"mfspr", OPTYPE_SPR, FL_OUT_D}}, + {467, &JitArm64::FallBackToInterpreter}, //"mtspr", OPTYPE_SPR, 0, 2}}, + {371, &JitArm64::FallBackToInterpreter}, //"mftb", OPTYPE_SYSTEM, FL_OUT_D | FL_TIMER}}, + {512, &JitArm64::FallBackToInterpreter}, //"mcrxr", OPTYPE_SYSTEM, 0}}, + {595, &JitArm64::FallBackToInterpreter}, //"mfsr", OPTYPE_SYSTEM, FL_OUT_D, 2}}, + {659, &JitArm64::FallBackToInterpreter}, //"mfsrin", OPTYPE_SYSTEM, FL_OUT_D, 2}}, + + {4, &JitArm64::Break}, //"tw", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, + {598, &JitArm64::FallBackToInterpreter}, //"sync", OPTYPE_SYSTEM, 0, 2}}, + {982, &JitArm64::icbi}, //"icbi", OPTYPE_SYSTEM, FL_ENDBLOCK, 3}}, + + // Unused instructions on GC + {310, &JitArm64::FallBackToInterpreter}, //"eciwx", OPTYPE_INTEGER, FL_RC_BIT}}, + {438, &JitArm64::FallBackToInterpreter}, //"ecowx", OPTYPE_INTEGER, FL_RC_BIT}}, + {854, &JitArm64::FallBackToInterpreter}, //"eieio", OPTYPE_INTEGER, FL_RC_BIT}}, + {306, &JitArm64::FallBackToInterpreter}, //"tlbie", OPTYPE_SYSTEM, 0}}, + {370, &JitArm64::FallBackToInterpreter}, //"tlbia", OPTYPE_SYSTEM, 0}}, + {566, &JitArm64::FallBackToInterpreter}, //"tlbsync", OPTYPE_SYSTEM, 0}}, +}; + +static GekkoOPTemplate table31_2[] = +{ + {266, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {778, &JitArm64::FallBackToInterpreter}, //"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {10, &JitArm64::FallBackToInterpreter}, //"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {522, &JitArm64::FallBackToInterpreter}, //"addcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {138, &JitArm64::FallBackToInterpreter}, //"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {650, &JitArm64::FallBackToInterpreter}, //"addeox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {234, &JitArm64::FallBackToInterpreter}, //"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {202, &JitArm64::FallBackToInterpreter}, //"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {491, &JitArm64::FallBackToInterpreter}, //"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {1003, &JitArm64::FallBackToInterpreter}, //"divwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {459, &JitArm64::FallBackToInterpreter}, //"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {971, &JitArm64::FallBackToInterpreter}, //"divwuox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 39}}, + {75, &JitArm64::FallBackToInterpreter}, //"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {11, &JitArm64::FallBackToInterpreter}, //"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {235, &JitArm64::FallBackToInterpreter}, //"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {747, &JitArm64::FallBackToInterpreter}, //"mullwox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT, 4}}, + {104, &JitArm64::FallBackToInterpreter}, //"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {40, &JitArm64::FallBackToInterpreter}, //"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {552, &JitArm64::FallBackToInterpreter}, //"subox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_RC_BIT}}, + {8, &JitArm64::FallBackToInterpreter}, //"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {520, &JitArm64::FallBackToInterpreter}, //"subfcox", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_SET_CA | FL_RC_BIT}}, + {136, &JitArm64::FallBackToInterpreter}, //"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {232, &JitArm64::FallBackToInterpreter}, //"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {200, &JitArm64::FallBackToInterpreter}, //"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_AB | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, +}; + +static GekkoOPTemplate table59[] = +{ + {18, &JitArm64::FallBackToInterpreter}, //{"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}}, + {20, &JitArm64::FallBackToInterpreter}, //"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, &JitArm64::FallBackToInterpreter}, //"faddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +// {22, &JitArm64::FallBackToInterpreter}, //"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {24, &JitArm64::FallBackToInterpreter}, //"fresx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, &JitArm64::FallBackToInterpreter}, //"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, &JitArm64::FallBackToInterpreter}, //"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, &JitArm64::FallBackToInterpreter}, //"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, &JitArm64::FallBackToInterpreter}, //"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, &JitArm64::FallBackToInterpreter}, //"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + +static GekkoOPTemplate table63[] = +{ + {264, &JitArm64::FallBackToInterpreter}, //"fabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {32, &JitArm64::FallBackToInterpreter}, //"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}}, + {0, &JitArm64::FallBackToInterpreter}, //"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}}, + {14, &JitArm64::FallBackToInterpreter}, //"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}}, + {15, &JitArm64::FallBackToInterpreter}, //"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}}, + {72, &JitArm64::FallBackToInterpreter}, //"fmrx", OPTYPE_FPU, FL_RC_BIT_F}}, + {136, &JitArm64::FallBackToInterpreter}, //"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {40, &JitArm64::FallBackToInterpreter}, //"fnegx", OPTYPE_FPU, FL_RC_BIT_F}}, + {12, &JitArm64::FallBackToInterpreter}, //"frspx", OPTYPE_FPU, FL_RC_BIT_F}}, + + {64, &JitArm64::FallBackToInterpreter}, //"mcrfs", OPTYPE_SYSTEMFP, 0}}, + {583, &JitArm64::FallBackToInterpreter}, //"mffsx", OPTYPE_SYSTEMFP, 0}}, + {70, &JitArm64::FallBackToInterpreter}, //"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}}, + {38, &JitArm64::FallBackToInterpreter}, //"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}}, + {134, &JitArm64::FallBackToInterpreter}, //"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}}, + {711, &JitArm64::FallBackToInterpreter}, //"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}}, +}; + +static GekkoOPTemplate table63_2[] = +{ + {18, &JitArm64::FallBackToInterpreter}, //"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}}, + {20, &JitArm64::FallBackToInterpreter}, //"fsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, &JitArm64::FallBackToInterpreter}, //"faddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {22, &JitArm64::FallBackToInterpreter}, //"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}}, + {23, &JitArm64::FallBackToInterpreter}, //"fselx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, &JitArm64::FallBackToInterpreter}, //"fmulx", OPTYPE_FPU, FL_RC_BIT_F}}, + {26, &JitArm64::FallBackToInterpreter}, //"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, &JitArm64::FallBackToInterpreter}, //"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, &JitArm64::FallBackToInterpreter}, //"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, &JitArm64::FallBackToInterpreter}, //"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, &JitArm64::FallBackToInterpreter}, //"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + + +namespace JitArm64Tables +{ + +void CompileInstruction(PPCAnalyst::CodeOp & op) +{ + JitArm64 *jitarm = (JitArm64 *)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] = &JitArm64::unknown_instruction; + } + + for (int i = 0; i < 1024; i++) + { + dynaOpTable4 [i] = &JitArm64::unknown_instruction; + dynaOpTable19[i] = &JitArm64::unknown_instruction; + dynaOpTable31[i] = &JitArm64::unknown_instruction; + dynaOpTable63[i] = &JitArm64::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/PowerPC/JitArm64/JitArm64_Tables.h b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.h new file mode 100644 index 0000000000..d1788e404e --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitArm64_Tables.h @@ -0,0 +1,14 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Core/PowerPC/Gekko.h" +#include "Core/PowerPC/PPCTables.h" + +namespace JitArm64Tables +{ + void CompileInstruction(PPCAnalyst::CodeOp & op); + void InitTables(); +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp new file mode 100644 index 0000000000..2b5e4a5982 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.cpp @@ -0,0 +1,80 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#include "Common/Arm64Emitter.h" + +#include "Core/PowerPC/PowerPC.h" +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitAsm.h" +#include "Core/PowerPC/JitCommon/JitCache.h" + +using namespace Arm64Gen; + +void JitArm64AsmRoutineManager::Generate() +{ + enterCode = GetCodePtr(); + + MOVI2R(X29, (u64)&PowerPC::ppcState); + + dispatcher = GetCodePtr(); + printf("Dispatcher 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_MI); + + 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(INDEX_UNSIGNED, W28, X29, PPCSTATE_OFF(pc)); // Load the current PC into W28 + BFM(W28, WSP, 3, 2); // Wipe the top 3 bits. Same as PC & JIT_ICACHE_MASK + + MOVI2R(X27, (u64)jit->GetBlockCache()->iCache); + LDR(W27, X27, X28); + + FixupBranch JitBlock = TBNZ(W27, 7); // Test the 7th bit + // Success, it is our Jitblock. + MOVI2R(X30, (u64)jit->GetBlockCache()->GetCodePointers()); + UBFM(X27, X27, 61, 60); // Same as X27 << 3 + LDR(X30, X30, X27); // Load the block address in to R14 + BR(X30); + // No need to jump anywhere after here, the block will go back to dispatcher start + + SetJumpTarget(JitBlock); + + MOVI2R(X30, (u64)&Jit); + BLR(X30); + + B(dispatcherNoCheck); + + SetJumpTarget(bail); + doTiming = GetCodePtr(); + MOVI2R(X30, (u64)&CoreTiming::Advance); + BLR(X30); + + // Does exception checking + LDR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(pc)); + STR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(npc)); + MOVI2R(X30, (u64)&PowerPC::CheckExceptions); + BLR(X30); + LDR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(npc)); + STR(INDEX_UNSIGNED, W0, X29, PPCSTATE_OFF(pc)); + + // Check the state pointer to see if we are exiting + // Gets checked on every exception check + MOVI2R(W0, (u64)PowerPC::GetStatePtr()); + LDR(INDEX_UNSIGNED, W0, W0, 0); + FixupBranch Exit = CBNZ(W0); + + B(dispatcher); + + SetJumpTarget(Exit); + + FlushIcache(); +} + +void JitArm64AsmRoutineManager::GenerateCommon() +{ +} diff --git a/Source/Core/Core/PowerPC/JitArm64/JitAsm.h b/Source/Core/Core/PowerPC/JitArm64/JitAsm.h new file mode 100644 index 0000000000..d2dba320c1 --- /dev/null +++ b/Source/Core/Core/PowerPC/JitArm64/JitAsm.h @@ -0,0 +1,29 @@ +// Copyright 2014 Dolphin Emulator Project +// Licensed under GPLv2 +// Refer to the license.txt file included. + +#pragma once + +#include "Common/Arm64Emitter.h" +#include "Core/PowerPC/JitCommon/JitAsmCommon.h" + +class JitArm64AsmRoutineManager : public CommonAsmRoutinesBase, public Arm64Gen::ARM64CodeBlock +{ +private: + void Generate(); + void GenerateCommon(); + +public: + void Init() + { + AllocCodeSpace(8192); + Generate(); + WriteProtect(); + } + + void Shutdown() + { + FreeCodeSpace(); + } +}; + diff --git a/Source/Core/Core/PowerPC/JitInterface.cpp b/Source/Core/Core/PowerPC/JitInterface.cpp index e7c1d1ed2a..ea9b12be70 100644 --- a/Source/Core/Core/PowerPC/JitInterface.cpp +++ b/Source/Core/Core/PowerPC/JitInterface.cpp @@ -29,6 +29,11 @@ #include "Core/PowerPC/JitArm32/JitArm_Tables.h" #endif +#if _M_ARM_64 +#include "Core/PowerPC/JitArm64/Jit.h" +#include "Core/PowerPC/JitArm64/JitArm64_Tables.h" +#endif + static bool bFakeVMEM = false; bool bMMU = false; @@ -66,6 +71,13 @@ namespace JitInterface break; } #endif + #if _M_ARM_64 + case 4: + { + ptr = new JitArm64(); + break; + } + #endif default: { PanicAlert("Unrecognizable cpu_core: %d", core); @@ -100,6 +112,13 @@ namespace JitInterface break; } #endif + #if _M_ARM_64 + case 4: + { + JitArm64Tables::InitTables(); + break; + } + #endif default: { PanicAlert("Unrecognizable cpu_core: %d", core); diff --git a/Source/Core/Core/PowerPC/PPCAnalyst.h b/Source/Core/Core/PowerPC/PPCAnalyst.h index 0916e3951e..64d24fdcca 100644 --- a/Source/Core/Core/PowerPC/PPCAnalyst.h +++ b/Source/Core/Core/PowerPC/PPCAnalyst.h @@ -69,6 +69,11 @@ struct BlockRegStats std::min(firstRead[reg], firstWrite[reg]); } + bool IsUsed(int reg) + { + return (numReads[reg] + numWrites[reg]) > 0; + } + inline void SetInputRegister(int reg, short opindex) { if (firstRead[reg] == -1)