diff --git a/Common/Arm64Emitter.cpp b/Common/Arm64Emitter.cpp index d843728116..61edcd9b88 100644 --- a/Common/Arm64Emitter.cpp +++ b/Common/Arm64Emitter.cpp @@ -1328,6 +1328,22 @@ void ARM64XEmitter::MVN(ARM64Reg Rd, ARM64Reg Rm) { ORN(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSL, 0)); } +void ARM64XEmitter::LSL(ARM64Reg Rd, ARM64Reg Rm, int shift) +{ + ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSL, shift)); +} +void ARM64XEmitter::LSR(ARM64Reg Rd, ARM64Reg Rm, int shift) +{ + ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_LSR, shift)); +} +void ARM64XEmitter::ASR(ARM64Reg Rd, ARM64Reg Rm, int shift) +{ + ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_ASR, shift)); +} +void ARM64XEmitter::ROR(ARM64Reg Rd, ARM64Reg Rm, int shift) +{ + ORR(Rd, Is64Bit(Rd) ? SP : WSP, Rm, ArithOption(Rm, ST_ROR, shift)); +} // Logical (immediate) void ARM64XEmitter::AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms) @@ -3023,6 +3039,7 @@ void ARM64XEmitter::ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) { AND(Rd, Rn, imm_r, imm_s); } else { + _assert_msg_(JIT, scratch != INVALID_REG, "ANDSI2R - failed to construct immediate value from %08x, need scratch", (u32)imm) MOVI2R(scratch, imm); AND(Rd, Rn, scratch); } @@ -3033,6 +3050,7 @@ void ARM64XEmitter::ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) { if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) { ORR(Rd, Rn, imm_r, imm_s); } else { + _assert_msg_(JIT, scratch != INVALID_REG, "ORI2R - failed to construct immediate value from %08x, need scratch", (u32)imm) MOVI2R(scratch, imm); ORR(Rd, Rn, scratch); } @@ -3043,6 +3061,7 @@ void ARM64XEmitter::ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch) if (IsImmLogical(imm, Is64Bit(Rn) ? 64 : 32, &n, &imm_s, &imm_r)) { ANDS(Rd, Rn, imm_r, imm_s); } else { + _assert_msg_(JIT, scratch != INVALID_REG, "ANDSI2R - failed to construct immediate value from %08x, need scratch", (u32)imm) MOVI2R(scratch, imm); ANDS(Rd, Rn, scratch); } diff --git a/Common/Arm64Emitter.h b/Common/Arm64Emitter.h index 913a3ec8e7..c06e65c39a 100644 --- a/Common/Arm64Emitter.h +++ b/Common/Arm64Emitter.h @@ -511,8 +511,13 @@ public: void ANDS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) { ANDS(Rd, Rn, Rm, ArithOption(Rd, 0)); } void BICS(ARM64Reg Rd, ARM64Reg Rn, ARM64Reg Rm) { BICS(Rd, Rn, Rm, ArithOption(Rd, 0)); } + // Convenience wrappers around ORR. These match the official convenience syntax. void MOV(ARM64Reg Rd, ARM64Reg Rm); void MVN(ARM64Reg Rd, ARM64Reg Rm); + void LSR(ARM64Reg Rd, ARM64Reg Rm, int shift); + void LSL(ARM64Reg Rd, ARM64Reg Rm, int shift); + void ASR(ARM64Reg Rd, ARM64Reg Rm, int shift); + void ROR(ARM64Reg Rd, ARM64Reg Rm, int shift); // Logical (immediate) void AND(ARM64Reg Rd, ARM64Reg Rn, u32 immr, u32 imms); @@ -621,12 +626,16 @@ public: // Wrapper around MOVZ+MOVK void MOVI2R(ARM64Reg Rd, u64 imm, bool optimize = true); + template + void MOVP2R(ARM64Reg Rd, P *ptr) { + MOVI2R(Rd, (uintptr_t)ptr); + } - // Wrapper around AND x, y, imm etc - void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch); - void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch); - void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch) { ANDSI2R(Is64Bit(Rn) ? SP : WSP, Rn, imm, scratch); } - void ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch); + // Wrapper around AND x, y, imm etc. If you are sure the imm will work, no need to pass a scratch register. + void ANDI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG); + void ANDSI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG); + void TSTI2R(ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG) { ANDSI2R(Is64Bit(Rn) ? SP : WSP, Rn, imm, scratch); } + void ORI2R(ARM64Reg Rd, ARM64Reg Rn, u64 imm, ARM64Reg scratch = INVALID_REG); // ABI related void ABI_PushRegisters(BitSet32 registers); diff --git a/Core/MIPS/ARM64/Arm64Asm.cpp b/Core/MIPS/ARM64/Arm64Asm.cpp index d70912d832..112d57c9f6 100644 --- a/Core/MIPS/ARM64/Arm64Asm.cpp +++ b/Core/MIPS/ARM64/Arm64Asm.cpp @@ -35,7 +35,23 @@ static const bool enableDebug = false; //static bool enableStatistics = false; //unused? -//The standard ARM calling convention allocates the 16 ARM registers as: + +// ARM64 calling conventions +// Standard: http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055b/IHI0055B_aapcs64.pdf +// Apple: https://developer.apple.com/library/ios/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html + +// Summary: +// =========== +// SP ("x31") is not a GPR so irrelevant. +// x0-x7: 8 parameter/result registers +// x8: "Indirect result location register" (points to struct return values? I think we can map this) +// x9-x15: 7 temporary registers (no need to save) +// x16: temporary register/procedure call scratch register 1 +// x17: temporary register/procedure call scratch register 2 +// x18: unavailable (reserved for use by the OS or linker or whatever - iOS, for example, uses it) +// x19-x28: 10 callee-saved registers +// x29: the frame pointer register +// x30: link register for procedure calls // r15 is the program counter. // r14 is the link register. (The BL instruction, used in a subroutine call, stores the return address in this register). @@ -44,13 +60,19 @@ static const bool enableDebug = false; // r4 to r11: used to hold local variables. // r0 to r3: used to hold argument values passed to a subroutine, and also hold results returned from a subroutine. -// Mappable registers: -// R2, R3, R4, R5, R6, R8, R11 +// So: Scratch registers: x16, x17 +// Mappable registers in priority order: +// x19, x20, x21, x22, x23, x24, x25, x27, x28, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15, x0, x1, +// That's a whole lot of registers so we might be able to statically allocate a bunch of common MIPS registers. +// We should put statically allocated registers in the 7 callee-save regs that are left over after the system regs (x19-x25), so we don't have to bother with +// saving them when we call out of the JIT. We will perform regular dynamic register allocation in the rest (x0-x15) + +// STATIC ALLOCATION ARM64 (these are all callee-save registers): +// x26 : JIT base reg +// x27 : MIPS state +// x28 : Memory base pointer. +// x29 : Down counter -// STATIC ALLOCATION ARM: -// R10 : MIPS state -// R11 : Memory base pointer. -// R7 : Down counter extern volatile CoreState coreState; void ShowPC(u32 sp) { @@ -71,12 +93,123 @@ namespace MIPSComp { using namespace Arm64JitConstants; -void Arm64Jit::GenerateFixedCode() -{ - // Uncomment if you want to see the output... - // INFO_LOG(JIT, "THE DISASM ========================"); - // DisassembleArm(enterCode, GetCodePtr() - enterCode); - // INFO_LOG(JIT, "END OF THE DISASM ========================"); +void Arm64Jit::GenerateFixedCode() { + enterCode = AlignCode16(); + + const u32 ALL_CALLEE_SAVED = 0x7FF80000; + BitSet32 regs_to_save(ALL_CALLEE_SAVED); + enterCode = GetCodePtr(); + + ABI_PushRegisters(regs_to_save); + + + // Fixed registers, these are always kept when in Jit context. + // R8 is used to hold flags during delay slots. Not always needed. + // R13 cannot be used as it's the stack pointer. + // TODO: Consider statically allocating: + // * r2-r4 + // Really starting to run low on registers already though... + + // R11, R10, R9 + MOVP2R(MEMBASEREG, Memory::base); + MOVP2R(CTXREG, mips_); + MOVP2R(JITBASEREG, GetBasePtr()); + + // TODO: Preserve ASIMD registers + + RestoreDowncount(); + MovFromPC(SCRATCH1); + outerLoopPCInR0 = GetCodePtr(); + MovToPC(SCRATCH1); + outerLoop = GetCodePtr(); + SaveDowncount(); + RestoreRoundingMode(true); + QuickCallFunction(SCRATCH1_64, &CoreTiming::Advance); + ApplyRoundingMode(true); + RestoreDowncount(); + FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time + + dispatcherCheckCoreState = GetCodePtr(); + + // The result of slice decrementation should be in flags if somebody jumped here + // IMPORTANT - We jump on negative, not carry!!! + FixupBranch bailCoreState = B(CC_MI); + + MOVP2R(SCRATCH1_64, &coreState); + LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0); + CMP(SCRATCH1, 0); + FixupBranch badCoreState = B(CC_NEQ); + FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time + + dispatcherPCInSCRATCH1 = GetCodePtr(); + // TODO: Do we always need to write PC to RAM here? + MovToPC(SCRATCH1); + + // At this point : flags = EQ. Fine for the next check, no need to jump over it. + dispatcher = GetCodePtr(); + + // 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); + + SetJumpTarget(skipToRealDispatch); + SetJumpTarget(skipToRealDispatch2); + + dispatcherNoCheck = GetCodePtr(); + + // Debug + if (enableDebug) { + // MOV(SCRATCH1, R13); + // QuickCallFunction(R1, (void *)&ShowPC); + } + + LDR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, pc)); + LDR(SCRATCH1, MEMBASEREG, SCRATCH1_64); + ANDI2R(SCRATCH2, SCRATCH1, 0xFF000000); // rotation is to the right, in 2-bit increments. + ANDI2R(SCRATCH1, SCRATCH1, 0x00FFFFFF); // TODO: Replace this and the next op by a bit field extract + LSR(SCRATCH2, SCRATCH2, 24); + CMP(SCRATCH2, MIPS_EMUHACK_OPCODE); + FixupBranch skipJump = B(CC_NEQ); + // IDEA - we have 26 bits, why not just use offsets from base of code? + // Another idea: Shift the bloc number left by two in the op, this would let us do + // LDR(R0, R9, R0); here, replacing the next instructions. +#ifdef IOS + // On iOS, R9 (JITBASEREG) is volatile. We have to reload it. + MOVI2R(JITBASEREG, (u32)GetBasePtr()); +#endif + ADD(SCRATCH1_64, SCRATCH1_64, JITBASEREG); + BR(SCRATCH1_64); + SetJumpTarget(skipJump); + // No block found, let's jit + SaveDowncount(); + RestoreRoundingMode(true); + QuickCallFunction(SCRATCH1_64, (void *)&MIPSComp::JitAt); + ApplyRoundingMode(true); + RestoreDowncount(); + + B(dispatcherNoCheck); // no point in special casing this + + SetJumpTarget(bail); + SetJumpTarget(bailCoreState); + + MOVP2R(SCRATCH1_64, &coreState); + LDR(INDEX_UNSIGNED, SCRATCH1, SCRATCH1_64, 0); + CMP(SCRATCH1, 0); + B(CC_EQ, outerLoop); + + SetJumpTarget(badCoreState); + breakpointBailout = GetCodePtr(); + + // TODO: Restore ASIMD registers + + SaveDowncount(); + RestoreRoundingMode(true); + + ABI_PopRegisters(regs_to_save); + + INFO_LOG(JIT, "THE DISASM ========================"); + DisassembleArm64(enterCode, GetCodePtr() - enterCode); + INFO_LOG(JIT, "END OF THE DISASM ========================"); // Don't forget to zap the instruction cache! FlushIcache(); diff --git a/Core/MIPS/ARM64/Arm64CompBranch.cpp b/Core/MIPS/ARM64/Arm64CompBranch.cpp index 3fd83a68b1..1baa33df81 100644 --- a/Core/MIPS/ARM64/Arm64CompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64CompBranch.cpp @@ -244,8 +244,8 @@ void Arm64Jit::BranchRSZeroComp(MIPSOpcode op, CCFlags cc, bool andLink, bool li // Take the branch if (andLink) { - gpr.SetRegImm(SCRATCHREG1, js.compilerPC + 8); - STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, MIPS_REG_RA * 4); + gpr.SetRegImm(SCRATCH1, js.compilerPC + 8); + STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, MIPS_REG_RA * 4); } WriteExit(targetAddr, js.nextExit++); diff --git a/Core/MIPS/ARM64/Arm64Jit.cpp b/Core/MIPS/ARM64/Arm64Jit.cpp index 2bd62769ea..0cb5d008ed 100644 --- a/Core/MIPS/ARM64/Arm64Jit.cpp +++ b/Core/MIPS/ARM64/Arm64Jit.cpp @@ -104,20 +104,20 @@ void Arm64Jit::FlushAll() void Arm64Jit::FlushPrefixV() { if ((js.prefixSFlag & JitState::PREFIX_DIRTY) != 0) { - gpr.SetRegImm(SCRATCHREG1, js.prefixS); - STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_SPREFIX])); + gpr.SetRegImm(SCRATCH1, js.prefixS); + STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_SPREFIX])); js.prefixSFlag = (JitState::PrefixState) (js.prefixSFlag & ~JitState::PREFIX_DIRTY); } if ((js.prefixTFlag & JitState::PREFIX_DIRTY) != 0) { - gpr.SetRegImm(SCRATCHREG1, js.prefixT); - STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_TPREFIX])); + gpr.SetRegImm(SCRATCH1, js.prefixT); + STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_TPREFIX])); js.prefixTFlag = (JitState::PrefixState) (js.prefixTFlag & ~JitState::PREFIX_DIRTY); } if ((js.prefixDFlag & JitState::PREFIX_DIRTY) != 0) { - gpr.SetRegImm(SCRATCHREG1, js.prefixD); - STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_DPREFIX])); + gpr.SetRegImm(SCRATCH1, js.prefixD); + STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, offsetof(MIPSState, vfpuCtrl[VFPU_CTRL_DPREFIX])); js.prefixDFlag = (JitState::PrefixState) (js.prefixDFlag & ~JitState::PREFIX_DIRTY); } } @@ -442,7 +442,7 @@ void Arm64Jit::WriteExit(u32 destination, int exit_num) b->linkStatus[exit_num] = true; } else { gpr.SetRegImm(X0, destination); - B((const void *)dispatcherPCInR0); + B((const void *)dispatcherPCInSCRATCH1); } } diff --git a/Core/MIPS/ARM64/Arm64Jit.h b/Core/MIPS/ARM64/Arm64Jit.h index fa4313d9d3..1f3ff56910 100644 --- a/Core/MIPS/ARM64/Arm64Jit.h +++ b/Core/MIPS/ARM64/Arm64Jit.h @@ -280,7 +280,7 @@ public: const u8 *outerLoop; const u8 *outerLoopPCInR0; const u8 *dispatcherCheckCoreState; - const u8 *dispatcherPCInR0; + const u8 *dispatcherPCInSCRATCH1; const u8 *dispatcher; const u8 *dispatcherNoCheck; diff --git a/Core/MIPS/ARM64/Arm64RegCache.cpp b/Core/MIPS/ARM64/Arm64RegCache.cpp index 43852bd070..bc5e58c06c 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.cpp +++ b/Core/MIPS/ARM64/Arm64RegCache.cpp @@ -328,8 +328,8 @@ void Arm64RegCache::FlushR(MIPSGPReg r) { case ML_IMM: // IMM is always "dirty". if (r != MIPS_REG_ZERO) { - SetRegImm(SCRATCHREG1, mr[r].imm); - emit_->STR(INDEX_UNSIGNED, SCRATCHREG1, CTXREG, GetMipsRegOffset(r)); + SetRegImm(SCRATCH1, mr[r].imm); + emit_->STR(INDEX_UNSIGNED, SCRATCH1, CTXREG, GetMipsRegOffset(r)); } break; diff --git a/Core/MIPS/ARM64/Arm64RegCache.h b/Core/MIPS/ARM64/Arm64RegCache.h index 1de81828f2..ec1068009b 100644 --- a/Core/MIPS/ARM64/Arm64RegCache.h +++ b/Core/MIPS/ARM64/Arm64RegCache.h @@ -24,12 +24,14 @@ namespace Arm64JitConstants { // Bogus mappings, TODO ARM64 -const Arm64Gen::ARM64Reg JITBASEREG = Arm64Gen::W0; -const Arm64Gen::ARM64Reg CTXREG = Arm64Gen::X1; -const Arm64Gen::ARM64Reg MEMBASEREG = Arm64Gen::X2; -const Arm64Gen::ARM64Reg SCRATCHREG1 = Arm64Gen::W3; -const Arm64Gen::ARM64Reg SCRATCHREG2 = Arm64Gen::W4; -const Arm64Gen::ARM64Reg DOWNCOUNTREG = Arm64Gen::W5; +const Arm64Gen::ARM64Reg JITBASEREG = Arm64Gen::X26; +const Arm64Gen::ARM64Reg CTXREG = Arm64Gen::X27; +const Arm64Gen::ARM64Reg MEMBASEREG = Arm64Gen::X28; +const Arm64Gen::ARM64Reg DOWNCOUNTREG = Arm64Gen::W29; // no need to use the full register width +const Arm64Gen::ARM64Reg SCRATCH1_64 = Arm64Gen::X16; +const Arm64Gen::ARM64Reg SCRATCH2_64 = Arm64Gen::X17; +const Arm64Gen::ARM64Reg SCRATCH1 = Arm64Gen::W16; +const Arm64Gen::ARM64Reg SCRATCH2 = Arm64Gen::W17; enum { TOTAL_MAPPABLE_MIPSREGS = 36, diff --git a/UI/DevScreens.cpp b/UI/DevScreens.cpp index 84992f52f0..9f68026f4e 100644 --- a/UI/DevScreens.cpp +++ b/UI/DevScreens.cpp @@ -631,6 +631,9 @@ void JitCompareScreen::UpdateDisasm() { } UI::EventReturn JitCompareScreen::OnAddressChange(UI::EventParams &e) { + if (!MIPSComp::jit) { + return UI::EVENT_DONE; + } JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); u32 addr; if (blockAddr_->GetText().size() > 8) @@ -666,6 +669,10 @@ UI::EventReturn JitCompareScreen::OnNextBlock(UI::EventParams &e) { } UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) { + if (!MIPSComp::jit) { + return UI::EVENT_DONE; + } + JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); if (Memory::IsValidAddress(e.a)) { currentBlock_ = blockCache->GetBlockNumberFromStartAddress(e.a); @@ -677,6 +684,10 @@ UI::EventReturn JitCompareScreen::OnBlockAddress(UI::EventParams &e) { } UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) { + if (!MIPSComp::jit) { + return UI::EVENT_DONE; + } + JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); int numBlocks = blockCache->GetNumBlocks(); if (numBlocks > 0) { @@ -687,6 +698,9 @@ UI::EventReturn JitCompareScreen::OnRandomBlock(UI::EventParams &e) { } UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) { + if (!MIPSComp::jit) { + return UI::EVENT_DONE; + } JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); int numBlocks = blockCache->GetNumBlocks(); if (numBlocks > 0) { @@ -714,6 +728,9 @@ UI::EventReturn JitCompareScreen::OnRandomVFPUBlock(UI::EventParams &e) { UI::EventReturn JitCompareScreen::OnCurrentBlock(UI::EventParams &e) { + if (!MIPSComp::jit) { + return UI::EVENT_DONE; + } JitBlockCache *blockCache = MIPSComp::jit->GetBlockCache(); std::vector blockNum; blockCache->GetBlockNumbersFromAddress(currentMIPS->pc, &blockNum); diff --git a/UI/MiscScreens.cpp b/UI/MiscScreens.cpp index 8d43317ea5..5ac270c5b4 100644 --- a/UI/MiscScreens.cpp +++ b/UI/MiscScreens.cpp @@ -439,6 +439,12 @@ void LogoScreen::render() { #ifdef _WIN32 dc.DrawText(screenManager()->getThin3DContext()->GetInfoString(T3DInfo::APINAME), bounds.centerX(), bounds.y2() - 100, colorAlpha(0xFFFFFFFF, alphaText), ALIGN_CENTER); +#elif defined(ANDROID) +#ifdef ARM64 + dc.SetFontScale(2.0f, 2.0f); + dc.DrawText("EXPERIMENTAL ARM64 BUILD", bounds.centerX(), bounds.y2() - 100, colorAlpha(0xFF3030FF, alphaText), ALIGN_CENTER); + dc.SetFontScale(1.0f, 1.0f); +#endif #endif dc.End();