From 265e70a498181df33a9f5e39e2f19f04c450adcc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 23 Nov 2012 19:41:35 +0100 Subject: [PATCH 01/49] Just some sketchwork on a JIT for ARM. When completed, will bring speed to mobile devices. --- CMakeLists.txt | 1 + Common/ArmABI.cpp | 15 +- Common/ArmABI.h | 2 +- Common/ArmCPUDetect.cpp | 153 +++++++++++ Common/ArmEmitter.cpp | 113 +++++++- Common/ArmEmitter.h | 85 ++++-- Common/CPUDetect.cpp | 12 +- Common/CPUDetect.h | 23 +- Common/Common.vcxproj | 6 + Common/Common.vcxproj.filters | 1 + Common/ThunkARM.cpp | 2 +- Core/MIPS/ARM/Asm.cpp | 30 +-- Core/MIPS/ARM/Asm.h | 3 - Core/MIPS/ARM/CompALU.cpp | 35 ++- Core/MIPS/ARM/CompBranch.cpp | 157 +++++------ Core/MIPS/ARM/Jit.cpp | 83 +++--- Core/MIPS/ARM/Jit.h | 15 +- Core/MIPS/ARM/RegCache.cpp | 466 +++++++++------------------------ Core/MIPS/ARM/RegCache.h | 178 ++++--------- Windows/PPSSPP.vcxproj | 10 +- Windows/PPSSPP.vcxproj.filters | 7 + android/jni/ARMEmitterTest.h | 5 + android/jni/Android.mk | 2 + android/jni/ArmEmitterTest.cpp | 75 ++++++ android/jni/NativeApp.cpp | 4 + 25 files changed, 822 insertions(+), 661 deletions(-) create mode 100644 Common/ArmCPUDetect.cpp create mode 100644 android/jni/ARMEmitterTest.h create mode 100644 android/jni/ArmEmitterTest.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 397d491bdd..2b521c8147 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,7 @@ if(ARM) set(CommonExtra ${CommonExtra} Common/ArmABI.h Common/ArmABI.cpp + Common/ArmCPUDetect.cpp Common/ArmEmitter.h Common/ArmEmitter.cpp Common/ThunkARM.cpp) diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index 63824d808f..fa86924dfb 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -47,6 +47,17 @@ void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) POP(5, R0, R1, R2, R3, _LR); } +void ARMXEmitter::ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3) +{ + ARMABI_MOVI2R(R14, Mem(func)); + PUSH(5, R0, R1, R2, R3, _LR); + ARMABI_MOVI2R(R0, Arg1); + ARMABI_MOVI2R(R1, Arg2); + ARMABI_MOVI2R(R2, Arg3); + BL(R14); + POP(5, R0, R1, R2, R3, _LR); +} + void ARMXEmitter::ARMABI_PushAllCalleeSavedRegsAndAdjustStack() { // Note: 4 * 4 = 16 bytes, so alignment is preserved. PUSH(4, R0, R1, R2, R3); @@ -55,6 +66,7 @@ void ARMXEmitter::ARMABI_PushAllCalleeSavedRegsAndAdjustStack() { void ARMXEmitter::ARMABI_PopAllCalleeSavedRegsAndAdjustStack() { POP(4, R0, R1, R2, R3); } + void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, Operand2 val) { // TODO: There are more fancy ways to save calls if we check if @@ -72,6 +84,7 @@ void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) MOVW(R12, op); MOVT(R12, op, true); STR(R12, R14); // R10 is what we want to store } + const char *conditions[] = {"EQ", "NEQ", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL" }; static void ShowCondition(u32 cond) { diff --git a/Common/ArmABI.h b/Common/ArmABI.h index 1646d10682..5f27879700 100644 --- a/Common/ArmABI.h +++ b/Common/ArmABI.h @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp new file mode 100644 index 0000000000..1e01732e23 --- /dev/null +++ b/Common/ArmCPUDetect.cpp @@ -0,0 +1,153 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "CPUDetect.h" +#include "StringUtil.h" + +const char procfile[] = "/proc/cpuinfo"; + +char *GetCPUString() +{ + const char marker[] = "Hardware\t: "; + char *cpu_string = 0; + // Count the number of processor lines in /proc/cpuinfo + char buf[1024]; + FILE *fp; + + fp = fopen(procfile, "r"); + if (!fp) + return 0; + + while (fgets(buf, sizeof(buf), fp)) + { + if (strncmp(buf, marker, sizeof(marker) - 1)) + continue; + cpu_string = buf + sizeof(marker) - 1; + cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline + break; + } + return cpu_string; +} +bool CheckCPUFeature(const char *feature) +{ + const char marker[] = "Features\t: "; + char buf[1024]; + FILE *fp; + + fp = fopen(procfile, "r"); + if (!fp) + return 0; + + while (fgets(buf, sizeof(buf), fp)) + { + if (strncmp(buf, marker, sizeof(marker) - 1)) + continue; + char *featurestring = buf + sizeof(marker) - 1; + char *token = strtok(featurestring, " "); + while (token != NULL) + { + if (strstr(token, feature)) + return true; + token = strtok(NULL, " "); + } + } + return false; +} +int GetCoreCount() +{ + const char marker[] = "processor\t: "; + int cores = 0; + char buf[1024]; + FILE *fp; + + fp = fopen(procfile, "r"); + if (!fp) + return 0; + + while (fgets(buf, sizeof(buf), fp)) + { + if (strncmp(buf, marker, sizeof(marker) - 1)) + continue; + ++cores; + } + return cores; +} + +CPUInfo cpu_info; + +CPUInfo::CPUInfo() { + Detect(); +} + +// Detects the various cpu features +void CPUInfo::Detect() +{ + // Set some defaults here + // When ARMv8 cpus come out, these need to be updated. + HTT = false; + OS64bit = false; + CPU64bit = false; + Mode64bit = false; + vendor = VENDOR_ARM; + + // Get the information about the CPU + strncpy(cpu_string, GetCPUString(), sizeof(cpu_string)); + num_cores = GetCoreCount(); + bSwp = CheckCPUFeature("swp"); + bHalf = CheckCPUFeature("half"); + bThumb = CheckCPUFeature("thumb"); + bFastMult = CheckCPUFeature("fastmult"); + bVFP = CheckCPUFeature("vfp"); + bEDSP = CheckCPUFeature("edsp"); + bThumbEE = CheckCPUFeature("thumbee"); + bNEON = CheckCPUFeature("neon"); + bVFPv3 = CheckCPUFeature("vfpv3"); + bTLS = CheckCPUFeature("tls"); + bVFPv4 = CheckCPUFeature("vfpv4"); + bIDIVa = CheckCPUFeature("idiva"); + bIDIVt = CheckCPUFeature("idivt"); + // These two are ARMv8 specific. + bFP = CheckCPUFeature("fp"); + bASIMD = CheckCPUFeature("asimd"); +} + +// Turn the cpu info into a string we can show +std::string CPUInfo::Summarize() +{ + std::string sum; + if (num_cores == 1) + sum = StringFromFormat("%s, %i core", cpu_string, num_cores); + else + sum = StringFromFormat("%s, %i cores", cpu_string, num_cores); + + if (bSwp) sum += ", SWP"; + if (bHalf) sum += ", Half"; + if (bThumb) sum += ", Thumb"; + if (bFastMult) sum += ", FastMult"; + if (bVFP) sum += ", VFP"; + if (bEDSP) sum += ", EDSP"; + if (bThumbEE) sum += ", ThumbEE"; + if (bNEON) sum += ", NEON"; + if (bVFPv3) sum += ", VFPv3"; + if (bTLS) sum += ", TLS"; + if (bVFPv4) sum += ", VFPv4"; + if (bIDIVa) sum += ", IDIVa"; + if (bIDIVt) sum += ", IDIVt"; + + return sum; +} diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 17ef8e711f..5de8fb6d7b 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -61,7 +61,7 @@ const u8 *ARMXEmitter::AlignCodePage() void ARMXEmitter::Flush() { - __builtin___clear_cache (startcode, code); + __clear_cache (startcode, code); SLEEP(0); } void ARMXEmitter::SetCC(CCFlags cond) @@ -315,6 +315,22 @@ void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, b } // Data Operations +void ARMXEmitter::WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2) +{ + Write32(condition | (0x7 << 24) | (Op << 20) | (dest << 16) | (Op2 << 12) | (r1 << 8) | (Op3 << 5) | (1 << 4) | r2); +} +void ARMXEmitter::UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor) +{ + if (!cpu_info.bIDIVa) + PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer."); + WriteSignedMultiply(3, 0xF, 0, dest, divisor, dividend); +} +void ARMXEmitter::SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor) +{ + if (!cpu_info.bIDIVa) + PanicAlert("Trying to use integer divide on hardware that doesn't support it. Bad programmer."); + WriteSignedMultiply(1, 0xF, 0, dest, divisor, dividend); +} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);} @@ -428,6 +444,99 @@ void ARMXEmitter::LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...) va_end(vl); WriteRegStoreOp(0x89, dest, WriteBack, RegList); } + +// NEON and ASIMD +void ARMXEmitter::VLDR(ARMReg Dest, ARMReg Base, Operand2 op) +{ + _assert_msg_(DYNA_REC, Dest >= S0 && Dest <= D31, "Passed Invalid dest register to VLDR"); + _assert_msg_(DYNA_REC, Base <= R15, "Passed invalid Base register to VLDR"); + bool single_reg = Dest < D0; + if (single_reg) + Dest = (ARMReg)(Dest - S0); + else + Dest = (ARMReg)(Dest - D0); + Write32(NO_COND | (13 << 24) | ((Dest & 0x10) << 18) | (1 << 20) | (Base << 16) \ + | (5 << 9) | (!single_reg << 8) | op.Imm8()); +} +void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) +{ + if (Dest > R15) + { + if (Src < S0) + { + if (Dest < D0) + { + // Moving to a Neon register FROM ARM Reg + Dest = (ARMReg)(Dest - S0); + Write32(NO_COND | (0xE0 << 20) | ((Dest & 0x1E) << 15) | (Src << 12) \ + | (0xA << 8) | ((Dest & 0x1) << 7) | (1 << 4)); + return; + } + else + { + // Move 64bit from Arm reg + _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM to NEON"); + } + } + } + else + { + if (Src > R15) + { + if (Src < D0) + { + // Moving to ARM Reg from Neon Register + Src = (ARMReg)(Src - S0); + Write32(NO_COND | (0xE1 << 20) | ((Src & 0x1E) << 15) | (Dest << 12) \ + | (0xA << 8) | ((Src & 0x1) << 7) | (1 << 4)); + + return; + } + else + { + // Move 64bit To Arm reg + _assert_msg_(DYNA_REC, false, "This VMOV doesn't support moving 64bit ARM From NEON"); + } + } + else + { + // Move Arm reg to Arm reg + _assert_msg_(DYNA_REC, false, "VMOV doesn't support moving ARM registers"); + } + } + // Moving NEON registers + int SrcSize = Src < D0 ? 1 : Src < Q0 ? 2 : 4; + int DestSize = Dest < D0 ? 1 : Dest < Q0 ? 2 : 4; + bool Single = DestSize == 1; + _assert_msg_(DYNA_REC, SrcSize == DestSize, "VMOV doesn't support moving different register sizes"); + if (Single) + { + Dest = (ARMReg)(Dest - S0); + Src = (ARMReg)(Src - S0); + Write32(NO_COND | (0x1D << 23) | ((Dest & 0x1) << 22) | (0x3 << 20) | ((Dest & 0x1E) << 11) \ + | (0x5 << 9) | (1 << 6) | ((Src & 0x1) << 5) | ((Src & 0x1E) >> 1)); + } + else + { + // Double and quad + bool Quad = DestSize == 4; + if (Quad) + { + // Gets encoded as a Double register + Dest = (ARMReg)((Dest - Q0) * 2); + Src = (ARMReg)((Src - Q0) * 2); + } + else + { + Dest = (ARMReg)(Dest - D0); + Src = (ARMReg)(Src - D0); + } + Write32((0xF2 << 24) | ((Dest & 0x10) << 18) | (1 << 21) | ((Src & 0xF) << 16) \ + | ((Dest & 0xF) << 12) | (1 << 8) | ((Src & 0x10) << 3) | (Quad << 6) \ + | ((Src & 0x10) << 1) | (1 << 4) | (Src & 0xF)); + } +} + // helper routines for setting pointers void ARMXEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2) { diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 0af0e636aa..a452e6fd4d 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -23,11 +23,12 @@ #include "Common.h" #include "MemoryUtil.h" -#undef _SP - namespace ArmGen { - +#undef _IP +#undef _SP +#undef _LR +#undef _PC enum ARMReg { // GPRs @@ -42,17 +43,21 @@ enum ARMReg // VFP single precision registers - S0 = 0, S1, S2, S3, S4, S5, S6, + S0, S1, S2, S3, S4, S5, S6, S7, S8, S9, S10, S11, S12, S13, S14, S15, S16, S17, S18, S19, S20, S21, S22, S23, S24, S25, S26, S27, S28, S29, S30, S31, // VFP Double Precision registers - D0 = 0, D1, D2, D3, D4, D5, D6, D7, + 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, + + // ASIMD Quad-Word registers + Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, + Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, INVALID_REG = 0xFFFFFFFF }; @@ -80,12 +85,12 @@ const u32 NO_COND = 0xE0000000; enum ShiftType { - LSL = 0, - ASL = 0, - LSR = 1, - ASR = 2, - ROR = 3, - RRX = 4 + ST_LSL = 0, + ST_ASL = 0, + ST_LSR = 1, + ST_ASR = 2, + ST_ROR = 3, + ST_RRX = 4 }; enum @@ -147,7 +152,7 @@ public: Operand2(ARMReg base, ShiftType type, ARMReg shift) // RSR { Type = TYPE_RSR; - _assert_msg_(DYNA_REC, type != RRX, "Invalid Operand2: RRX does not take a register shift amount"); + _assert_msg_(DYNA_REC, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); IndexOrShift = shift; Shift = type; Value = base; @@ -158,31 +163,31 @@ public: if(shift == 32) shift = 0; switch (type) { - case LSL: + case ST_LSL: _assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSL %u", shift); break; - case LSR: + case ST_LSR: _assert_msg_(DYNA_REC, shift <= 32, "Invalid Operand2: LSR %u", shift); if (!shift) - type = LSL; + type = ST_LSL; if (shift == 32) shift = 0; break; - case ASR: + case ST_ASR: _assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSR %u", shift); if (!shift) - type = LSL; + type = ST_LSL; if (shift == 32) shift = 0; break; - case ROR: + case ST_ROR: _assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: ROR %u", shift); if (!shift) - type = LSL; + type = ST_LSL; break; - case RRX: + case ST_RRX: _assert_msg_(DYNA_REC, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); - type = ROR; + type = ST_ROR; break; } IndexOrShift = shift; @@ -228,6 +233,11 @@ public: _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm5 not IMM value"); return ((Value & 0x0000001F) << 7); } + const u32 Imm8() + { + _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value"); + return Value & 0xFF; + } const u32 Imm8Rot() // IMM8 with Rotation { _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value"); @@ -269,6 +279,17 @@ public: _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM"); return (Value & 0x0FFFFFFF); } + // NEON and ASIMD specific + const u32 Imm8ASIMD() + { + _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8ASIMD not IMM"); + return ((Value & 0x80) << 17) | ((Value & 0x70) << 12) | (Value & 0xF); + } + const u32 Imm8VFP() + { + _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8VFP not IMM"); + return ((Value & 0xF0) << 12) | (Value & 0xF); + } }; @@ -300,6 +321,7 @@ private: void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2); void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2); + void WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2); // New Ops void WriteInstruction(u32 op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags = false); @@ -385,13 +407,17 @@ public: void ORRS(ARMReg dest, ARMReg src, Operand2 op2); void MOV (ARMReg dest, Operand2 op2); void MOVS(ARMReg dest, Operand2 op2); - void BIC (ARMReg dest, ARMReg src, Operand2 op2); + void BIC (ARMReg dest, ARMReg src, Operand2 op2); // BIC = ANDN void BICS(ARMReg dest, ARMReg src, Operand2 op2); void MVN (ARMReg dest, Operand2 op2); void MVNS(ARMReg dest, Operand2 op2); void MOVW(ARMReg dest, Operand2 op2); void MOVT(ARMReg dest, Operand2 op2, bool TopBits = false); + // UDIV and SDIV are only available on CPUs that have + // the idiva hardare capacity + void UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor); + void SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor); void MUL (ARMReg dest, ARMReg src, ARMReg op2); void MULS(ARMReg dest, ARMReg src, ARMReg op2); @@ -422,18 +448,29 @@ public: // dest contains the result if the instruction managed to store the value void STREX(ARMReg dest, ARMReg base, ARMReg op); void DMB (); - + + // NEON and ASIMD instructions + // None of these will be created with conditional since ARM + // is deprecating conditional execution of ASIMD instructions. + // Some ASIMD instructions don't even have a conditional encoding. + + void VLDR(ARMReg dest, ARMReg Base, Operand2 op); + void VMOV(ARMReg Dest, ARMReg Src); + // Utility functions // The difference between this and CALL is that this aligns the stack // where appropriate. void ARMABI_CallFunction(void *func); void ARMABI_CallFunctionC(void *func, u32 Arg0); void ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2); + void ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3); void ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); void ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); void ARMABI_MOVI2R(ARMReg reg, Operand2 val); void ARMABI_MOVI2M(Operand2 op, Operand2 val); + void ARMABI_MOVI2M(u32 addr, Operand2 val); void ARMABI_ShowConditions(); + void ARMABI_Return(); void UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval); diff --git a/Common/CPUDetect.cpp b/Common/CPUDetect.cpp index ea04ca0704..219b0f5bd7 100644 --- a/Common/CPUDetect.cpp +++ b/Common/CPUDetect.cpp @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -30,7 +30,9 @@ #else //#include +#ifndef _M_GENERIC #include +#endif #if defined __FreeBSD__ #include @@ -39,7 +41,9 @@ static inline void do_cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, unsigned int *edx) { -#ifdef _LP64 +#if defined _M_GENERIC + (*eax) = (*ebx) = (*ecx) = (*edx) = 0; +#elif defined _LP64 // Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to // restored at the end of the asm block. __asm__ ( @@ -93,10 +97,6 @@ CPUInfo::CPUInfo() { Detect(); } -#ifdef _WIN32 -#include -#endif - // Detects the various cpu features void CPUInfo::Detect() { diff --git a/Common/CPUDetect.h b/Common/CPUDetect.h index 5b98ab4805..858c57e680 100644 --- a/Common/CPUDetect.h +++ b/Common/CPUDetect.h @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of @@ -25,7 +25,8 @@ enum CPUVendor { VENDOR_INTEL = 0, VENDOR_AMD = 1, - VENDOR_OTHER = 2, + VENDOR_ARM = 2, + VENDOR_OTHER = 3, }; struct CPUInfo @@ -55,6 +56,24 @@ struct CPUInfo bool bAES; bool bLAHFSAHF64; bool bLongMode; + + // ARM specific CPUInfo + bool bSwp; + bool bHalf; + bool bThumb; + bool bFastMult; + bool bVFP; + bool bEDSP; + bool bThumbEE; + bool bNEON; + bool bVFPv3; + bool bTLS; + bool bVFPv4; + bool bIDIVa; + bool bIDIVt; + // ARMv8 specific + bool bFP; + bool bASIMD; // Call Detect() explicit CPUInfo(); diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index c61737636a..6417d93216 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -203,6 +203,12 @@ true true + + true + true + true + true + true true diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 93673cb372..8e09f10993 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -74,6 +74,7 @@ + diff --git a/Common/ThunkARM.cpp b/Common/ThunkARM.cpp index b470f868e8..98cee1683d 100644 --- a/Common/ThunkARM.cpp +++ b/Common/ThunkARM.cpp @@ -2,7 +2,7 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of diff --git a/Core/MIPS/ARM/Asm.cpp b/Core/MIPS/ARM/Asm.cpp index da68215c7b..9072857f8a 100644 --- a/Core/MIPS/ARM/Asm.cpp +++ b/Core/MIPS/ARM/Asm.cpp @@ -81,6 +81,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) #endif */ + PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); SetCC(CC_AL); //ARMABI_MOVIMM32(R11, (u32)Memory::base); //ARMABI_MOVIMM32(R10, (u32)jit->GetBlockCache()->GetCodePointers()); @@ -107,26 +108,23 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //ARMABI_MOVIMM32(R1, Memory::MEMVIEW32_MASK + 1); AND(R0, R0, R1); LDR(R0, R11, R0); - MOV(R1, R0); - //AND(R1, R1, Imm32(MIPS_EMUHACK_MASK)); - //AND(R0, R0, Imm32(MIPS_EMUHACK_VALUE_MASK)); - //CMP(R0, Imm32(MIPS_EMUHACK_OPCODE)); - SetCC(CC_NEQ); - FixupBranch notfound = B(); - SetCC(CC_AL); + AND(R1, R0, Operand2(0xFC, 24)); + BIC(R0, R0, Operand2(0xFC, 24)); + CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 24)); + FixupBranch notfound = B_CC(CC_NEQ); // IDEA - we have 24 bits, why not just use offsets from base of code? if (enableDebug) { - // ADD(32, M(&mips->debugCount), Imm8(1)); + //ADD(32, M(&mips->debugCount), Imm8(1)); } - //grab from list and jump to it - // ADD(R0, R10, LSL(R0, 2)); + // grab from list and jump to it + ADD(R0, R10, Operand2(2, ST_LSL, R0)); + LDR(R0, R0); B(R0); SetJumpTarget(notfound); - //BL(&Jit); - - //B(dispatcherNoCheck); // no point in special casing this + ARMABI_CallFunction((void *)&Jit); + B(dispatcherNoCheck); // no point in special casing this SetJumpTarget(bail); doTiming = GetCodePtr(); @@ -148,11 +146,9 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); B(_LR); - breakpointBailout = GetCodePtr(); + PUSH(8, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns + - //Landing pad for drec space - //ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); - //RET(); GenerateCommon(); } diff --git a/Core/MIPS/ARM/Asm.h b/Core/MIPS/ARM/Asm.h index 4897d885f4..481e46374b 100644 --- a/Core/MIPS/ARM/Asm.h +++ b/Core/MIPS/ARM/Asm.h @@ -72,10 +72,7 @@ public: const u8 *fpException; const u8 *testExceptions; const u8 *testExternalExceptions; - const u8 *dispatchPcInEAX; const u8 *doTiming; - - const u8 *breakpointBailout; }; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/CompALU.cpp b/Core/MIPS/ARM/CompALU.cpp index da608b6655..5560da52e4 100644 --- a/Core/MIPS/ARM/CompALU.cpp +++ b/Core/MIPS/ARM/CompALU.cpp @@ -151,10 +151,7 @@ namespace MIPSComp int rs = _RS; int rd = _RD; - gpr.Lock(rd, rs, rt); - gpr.BindToRegister(rs, true, false); - gpr.BindToRegister(rt, true, false); - gpr.BindToRegister(rd, true, true); + // gpr.Lock(rd, rs, rt); switch (op & 63) { @@ -163,42 +160,42 @@ namespace MIPSComp // case 32: //R(rd) = R(rs) + R(rt); break; //add case 33: //R(rd) = R(rs) + R(rt); break; //addu - ADD(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); + ADD(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 134: //R(rd) = R(rs) - R(rt); break; //sub case 135: - SUB(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); + SUB(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 136: //R(rd) = R(rs) & R(rt); break; //and - AND(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); + AND(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 137: //R(rd) = R(rs) | R(rt); break; //or - ORR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); + ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 138: //R(rd) = R(rs) ^ R(rt); break; //xor/eor - EOR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); + EOR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 39: // R(rd) = ~(R(rs) | R(rt)); //nor - ORR(gpr.RX(rd), gpr.RX(rs), gpr.RX(rt)); - MVN(gpr.RX(rd), gpr.RX(rd)); + ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); + MVN(gpr.R(rd), gpr.R(rd)); break; case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt - CMP(gpr.RX(rs), gpr.RX(rt)); + CMP(gpr.R(rs), gpr.R(rt)); SetCC(CC_LT); - ARMABI_MOVI2R(gpr.RX(rd), 1); + ARMABI_MOVI2R(gpr.R(rd), 1); SetCC(CC_GE); - ARMABI_MOVI2R(gpr.RX(rd), 0); + ARMABI_MOVI2R(gpr.R(rd), 0); SetCC(CC_AL); break; case 43: //R(rd) = R(rs) < R(rt); break; //sltu - CMP(gpr.RX(rs), gpr.RX(rt)); + CMP(gpr.R(rs), gpr.R(rt)); SetCC(CC_LO); - ARMABI_MOVI2R(gpr.RX(rd), 1); + ARMABI_MOVI2R(gpr.R(rd), 1); SetCC(CC_HS); - ARMABI_MOVI2R(gpr.RX(rd), 0); + ARMABI_MOVI2R(gpr.R(rd), 0); SetCC(CC_AL); break; @@ -209,11 +206,11 @@ namespace MIPSComp // CMP(a,b); CMOVGT(a,b) default: - gpr.UnlockAll(); + // gpr.UnlockAll(); Comp_Generic(op); break; } - gpr.UnlockAll(); + // gpr.UnlockAll(); } diff --git a/Core/MIPS/ARM/CompBranch.cpp b/Core/MIPS/ARM/CompBranch.cpp index e9112d5fcb..7c110ff4cd 100644 --- a/Core/MIPS/ARM/CompBranch.cpp +++ b/Core/MIPS/ARM/CompBranch.cpp @@ -41,8 +41,8 @@ using namespace MIPSAnalyst; namespace MIPSComp { - /* -void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) + +void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) { int offset = (signed short)(op&0xFFFF)<<2; int rt = _RT; @@ -61,28 +61,32 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) if (rs == 0) { - CMP(32, gpr.R(rt), Imm32(0)); + CMP(gpr.R(rs), Operand2(0)); } else { - gpr.BindToRegister(rs, true, false); - CMP(32, gpr.R(rs), rt == 0 ? Imm32(0) : gpr.R(rt)); + CMP(gpr.R(rs), gpr.R(rt)); } FlushAll(); js.inDelaySlot = true; - Gen::FixupBranch ptr; + ArmGen::FixupBranch ptr; if (!likely) { - PUSHF(); // preserve flag around the delay slot! + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where + // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the + // delay slot, we're screwed. + MRS(R0); // Save flags register + PUSH(1, R0); CompileAt(js.compilerPC + 4); FlushAll(); - POPF(); // restore flag! - ptr = J_CC(cc, true); + POP(1, R0); + _MSR(false, false, R0); // Restore flags register + ptr = B_CC(cc); } else { - ptr = J_CC(cc, true); + ptr = B_CC(cc); CompileAt(js.compilerPC + 4); FlushAll(); } @@ -97,9 +101,9 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) js.compiling = false; } -*/ - /* -void Jit::BranchRSZeroComp(u32 op, Gen::CCFlags cc, bool likely) + + +void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) { int offset = (signed short)(op&0xFFFF)<<2; int rs = _RS; @@ -110,26 +114,31 @@ void Jit::BranchRSZeroComp(u32 op, Gen::CCFlags cc, bool likely) bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs; if (!delaySlotIsNice) { - ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC); + // ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC); } - gpr.BindToRegister(rs, true, false); - CMP(32, gpr.R(rs), Imm32(0)); + CMP(gpr.R(rs), Operand2(0)); FlushAll(); - Gen::FixupBranch ptr; js.inDelaySlot = true; - if (!likely) + ArmGen::FixupBranch ptr; + if (!likely) { - PUSHF(); // preserve flag around the delay slot! Better hope the delay slot instruction doesn't need to fall back to interpreter... + // preserve flag around the delay slot! Maybe this is not always necessary on ARM where + // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the + // delay slot, we're screwed. + MRS(R0); // Save flags register + PUSH(1, R0); CompileAt(js.compilerPC + 4); FlushAll(); - POPF(); // restore flag! - ptr = J_CC(cc, true); + + POP(1, R0); + _MSR(false, false, R0); // Restore flags register + ptr = B_CC(cc); } else { - ptr = J_CC(cc, true); + ptr = B_CC(cc); CompileAt(js.compilerPC + 4); FlushAll(); } @@ -143,54 +152,50 @@ void Jit::BranchRSZeroComp(u32 op, Gen::CCFlags cc, bool likely) WriteExit(js.compilerPC + 8, 1); js.compiling = false; } -*/ + void Jit::Comp_RelBranch(u32 op) { - /* + // The CC flags here should be opposite of the actual branch becuase they skip the branching action. switch (op>>26) { - case 4: BranchRSRTComp(op, CC_NZ, false); break;//beq - case 5: BranchRSRTComp(op, CC_Z, false); break;//bne + case 4: BranchRSRTComp(op, CC_NEQ, false); break;//beq + case 5: BranchRSRTComp(op, CC_EQ, false); break;//bne - case 6: BranchRSZeroComp(op, CC_G, false); break;//blez + case 6: BranchRSZeroComp(op, CC_GT, false); break;//blez case 7: BranchRSZeroComp(op, CC_LE, false); break;//bgtz - case 20: BranchRSRTComp(op, CC_NZ, true); break;//beql - case 21: BranchRSRTComp(op, CC_Z, true); break;//bnel + case 20: BranchRSRTComp(op, CC_NEQ, true); break;//beql + case 21: BranchRSRTComp(op, CC_EQ, true); break;//bnel - case 22: BranchRSZeroComp(op, CC_G, true); break;//blezl + case 22: BranchRSZeroComp(op, CC_GT, true); break;//blezl case 23: BranchRSZeroComp(op, CC_LE, true); break;//bgtzl default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); break; } - */ js.compiling = false; } void Jit::Comp_RelBranchRI(u32 op) { - /* switch ((op >> 16) & 0x1F) { case 0: BranchRSZeroComp(op, CC_GE, false); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 4; break;//bltz - case 1: BranchRSZeroComp(op, CC_L, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez + case 1: BranchRSZeroComp(op, CC_LT, false); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 4; break;//bgez case 2: BranchRSZeroComp(op, CC_GE, true); break; //if ((s32)R(rs) < 0) DelayBranchTo(addr); else PC += 8; break;//bltzl - case 3: BranchRSZeroComp(op, CC_L, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl + case 3: BranchRSZeroComp(op, CC_LT, true); break; //if ((s32)R(rs) >= 0) DelayBranchTo(addr); else PC += 8; break;//bgezl default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); break; } - */ js.compiling = false; } // If likely is set, discard the branch slot if NOT taken. -/* -void Jit::BranchFPFlag(u32 op, Gen::CCFlags cc, bool likely) +void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) { int offset = (signed short)(op & 0xFFFF) << 2; u32 targetAddr = js.compilerPC + offset + 4; @@ -204,20 +209,27 @@ void Jit::BranchFPFlag(u32 op, Gen::CCFlags cc, bool likely) } FlushAll(); - TEST(32, M((void *)&(mips_->fpcond)), Imm32(1)); - Gen::FixupBranch ptr; + ARMABI_MOVI2R(R0, (u32)&(mips_->fpcond)); + LDR(R0, R0, Operand2(0, TYPE_IMM)); + TST(R0, Operand2(1, TYPE_IMM)); + ArmGen::FixupBranch ptr; js.inDelaySlot = true; if (!likely) { - PUSHF(); // preserve flag around the delay slot! + MRS(R0); // Save flags register + PUSH(1, R0); + CompileAt(js.compilerPC + 4); FlushAll(); - POPF(); // restore flag! - ptr = J_CC(cc, true); + + // POPF(); // restore flag! + POP(1, R0); + _MSR(false, false, R0); // Restore flags register + ptr = B_CC(cc); } else { - ptr = J_CC(cc, true); + ptr = B_CC(cc); CompileAt(js.compilerPC + 4); FlushAll(); } @@ -229,26 +241,21 @@ void Jit::BranchFPFlag(u32 op, Gen::CCFlags cc, bool likely) SetJumpTarget(ptr); // Not taken WriteExit(js.compilerPC + 8, 1); - js.compiling = false; } -*/ - void Jit::Comp_FPUBranch(u32 op) { - /* switch((op >> 16) & 0x1f) { - case 0: BranchFPFlag(op, CC_NZ, false); break; // bc1f - case 1: BranchFPFlag(op, CC_Z, false); break; // bc1t - case 2: BranchFPFlag(op, CC_NZ, true); break; // bc1fl - case 3: BranchFPFlag(op, CC_Z, true); break; // bc1tl + case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f + case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t + case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl + case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); break; } - */ js.compiling = false; } @@ -274,13 +281,12 @@ void Jit::Comp_VBranch(u32 op) //case 3: if ( val) DelayBranchTo(addr); else PC += 8; break; //bvtl //TODO } - js.compiling = false; - */ + */ + js.compiling = false; } void Jit::Comp_Jump(u32 op) { - /* u32 off = ((op & 0x3FFFFFF) << 2); u32 targetAddr = (js.compilerPC & 0xF0000000) | off; //Delay slot @@ -294,7 +300,9 @@ void Jit::Comp_Jump(u32 op) break; case 3: //jal - MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8)); // Save return address + ARMABI_MOVI2R(R0, Operand2(js.compilerPC + 8, TYPE_IMM)); + ARMABI_MOVI2R(R1, Operand2((u32)&mips_->r[MIPS_REG_RA], TYPE_IMM)); + STR(R1, R0); WriteExit(targetAddr, 0); break; @@ -303,54 +311,55 @@ void Jit::Comp_Jump(u32 op) break; } js.compiling = false; - */ } void Jit::Comp_JumpReg(u32 op) { - /* - int rs = _RS; - u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); - bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs; + bool delaySlotIsNice = GetOutReg(delaySlotOp) != _RS; // Do what with that information? - gpr.BindToRegister(rs, true, false); - PUSH(32, gpr.R(rs)); + ARMReg rs = gpr.R(_RS); + + // Delay slot + PUSH(1, rs); // Save the destination address through the delay slot. Could use isNice to avoid CompileAt(js.compilerPC + 4); FlushAll(); - POP(32, R(EAX)); + POP(1, R0); switch (op & 0x3f) { case 8: //jr break; case 9: //jalr - MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8)); + ARMABI_MOVI2R(R1, (u32)&mips_->r[MIPS_REG_RA]); + ARMABI_MOVI2R(R2, js.compilerPC + 8); + STR(R1, R2); + // MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8)); break; default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); break; } - WriteExitDestInEAX(); - js.compiling = false; - */ + WriteExitDestInR(R0); + js.compiling = false; } void Jit::Comp_Syscall(u32 op) { - /* FlushAll(); - MOV(32, R(EAX), M(&mips_->r[MIPS_REG_RA])); - MOV(32, M(&mips_->pc), R(EAX)); + // By putting mips_ in a register and using offsets, we could get rid of one of the constant-sets. + ARMABI_MOVI2R(R0, (u32)&mips_->r[MIPS_REG_RA]); + LDR(R0, R0); + ARMABI_MOVI2R(R1, (u32)&mips_->pc); + STR(R1, R0); - ABI_CallFunctionC(&CallSyscall, op); - - WriteSyscallExit();*/ + ARMABI_CallFunctionC((void *)&CallSyscall, op); + WriteSyscallExit(); } } // namespace Mipscomp diff --git a/Core/MIPS/ARM/Jit.cpp b/Core/MIPS/ARM/Jit.cpp index c1e55cada4..d3c33f61c7 100644 --- a/Core/MIPS/ARM/Jit.cpp +++ b/Core/MIPS/ARM/Jit.cpp @@ -62,20 +62,20 @@ void MovToReg(int reg, u32 value) } */ -Jit::Jit(MIPSState *mips) : blocks(mips), mips_(mips) +Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips) { blocks.Init(); asm_.Init(mips, this); gpr.SetEmitter(this); - fpr.SetEmitter(this); + //fpr.SetEmitter(this); AllocCodeSpace(1024 * 1024 * 16); } void Jit::FlushAll() { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); + gpr.Flush(); + //fpr.Flush(FLUSH_ALL); } void Jit::ClearCache() @@ -124,8 +124,8 @@ const u8 *Jit::DoJit(u32 em_address, JitBlock *b) // TODO: this needs work MIPSAnalyst::AnalysisResults analysis; // = MIPSAnalyst::Analyze(em_address); - gpr.Start(mips_, analysis); - fpr.Start(mips_, analysis); + gpr.Start(analysis); + //fpr.Start(mips_, analysis); int numInstructions = 0; int cycles = 0; @@ -164,10 +164,42 @@ void Jit::Comp_Generic(u32 op) } } +void Jit::DoDownCount() +{ + ARMReg A = gpr.GetReg(); + ARMReg B = gpr.GetReg(); + ARMABI_MOVI2R(A, Mem(&CoreTiming::downcount)); + LDR(B, A); + if(js.downcountAmount < 255) // We can enlarge this if we used rotations + { + SUBS(B, B, js.downcountAmount); + STR(A, B); + } + else + { + ARMReg C = gpr.GetReg(false); + ARMABI_MOVI2R(C, js.downcountAmount); + SUBS(B, B, C); + STR(A, B); + } + gpr.Unlock(A, B); +} + +void Jit::WriteExitDestInR(ARMReg Reg) +{ + ARMReg A = gpr.GetReg(); + ARMABI_MOVI2R(A, (u32)&mips_->pc); + STR(A, Reg); + gpr.Unlock(Reg); // This was locked in the instruction beforehand. + DoDownCount(); + ARMABI_MOVI2R(A, (u32)asm_.dispatcher); + B(A); + gpr.Unlock(A); +} + void Jit::WriteExit(u32 destination, int exit_num) { - //SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - + 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; @@ -178,41 +210,28 @@ void Jit::WriteExit(u32 destination, int exit_num) if (block >= 0 && jo.enableBlocklink) { // It exists! Joy of joy! - //JMP(blocks.GetBlock(block)->checkedEntry, true); + B(blocks.GetBlock(block)->checkedEntry); b->linkStatus[exit_num] = true; } else { - //MOV(32, M(&mips_->pc), Imm32(destination)); - //JMP(asm_.dispatcher, true); + ARMABI_MOVI2R(R0, (u32)&mips_->pc); // Watch out! This uses R14 and R12! + ARMABI_MOVI2R(R1, destination); // Watch out! This uses R14 and R12! + STR(R0, R1); // Watch out! This uses R14 and R12! + ARMReg A = gpr.GetReg(false); + ARMABI_MOVI2R(A, (u32)asm_.dispatcher); + B(A); } } -void Jit::WriteExitDestInEAX() -{ - /* - MOV(32, M(&mips_->pc), R(EAX)); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(asm_.dispatcher, true); - */ -} -/* -void Jit::WriteRfiExitDestInEAX() -{ - MOV(32, M(&mips_->pc), R(EAX)); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(asm_routines.testExceptions, true); -}*/ - void Jit::WriteSyscallExit() { - - /* - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(asm_.dispatcher, true); - */ + // Super basic + DoDownCount(); + B((const void *)asm_.dispatcher); } + #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) #define _RD ((op>>11) & 0x1F) diff --git a/Core/MIPS/ARM/Jit.h b/Core/MIPS/ARM/Jit.h index fe81735e41..4c9c0f695b 100644 --- a/Core/MIPS/ARM/Jit.h +++ b/Core/MIPS/ARM/Jit.h @@ -98,15 +98,16 @@ private: void ClearCache(); void FlushAll(); + void DoDownCount(); + void WriteExit(u32 destination, int exit_num); - void WriteExitDestInEAX(); -// void WriteRfiExitDestInEAX(); + void WriteExitDestInR(ARMReg Reg); void WriteSyscallExit(); // Utility compilation functions - //void BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely); - //void BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely); - //void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely); + void BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely); + void BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely); + void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely); // Utilities to reduce duplicated code /* @@ -122,8 +123,8 @@ private: JitOptions jo; JitState js; - GPRRegCache gpr; - FPURegCache fpr; + ArmRegCache gpr; + // FPURegCache fpr; AsmRoutineManager asm_; diff --git a/Core/MIPS/ARM/RegCache.cpp b/Core/MIPS/ARM/RegCache.cpp index e562f78411..1a6c966298 100644 --- a/Core/MIPS/ARM/RegCache.cpp +++ b/Core/MIPS/ARM/RegCache.cpp @@ -2,11 +2,11 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. @@ -15,395 +15,171 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#include "../MIPS.h" -#include "../MIPSTables.h" -#include "../MIPSAnalyst.h" -#include "Jit.h" -#include "Asm.h" #include "RegCache.h" -#include "CommonFuncs.h" +#include "ArmEmitter.h" using namespace ArmGen; -RegCache::RegCache() : emit(0) { - memset(locks, 0, sizeof(locks)); - memset(xlocks, 0, sizeof(xlocks)); - memset(saved_locks, 0, sizeof(saved_locks)); - memset(saved_xlocks, 0, sizeof(saved_xlocks)); - memset(regs, 0, sizeof(regs)); - memset(xregs, 0, sizeof(xregs)); - memset(saved_regs, 0, sizeof(saved_regs)); - memset(saved_xregs, 0, sizeof(saved_xregs)); +ArmRegCache::ArmRegCache(MIPSState *mips) + : mips_(mips) +{ + emit = 0; + } -static const int allocationOrder[] = +void ArmRegCache::Init(ARMXEmitter *emitter) { - R2, R3, R4, R5, R6, R7, R8, R10, R11 // omitting R9? -}; - -void RegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) -{ - for (int i = 0; i < NUMARMREGS; i++) + emit = emitter; + ARMReg *PPCRegs = GetMIPSAllocationOrder(NUMMIPSREG); + ARMReg *Regs = GetAllocationOrder(NUMARMREG); + for(int a = 0; a < 32; ++a) { - xregs[i].free = true; - xregs[i].dirty = false; - xlocks[i] = false; + // This gives us the memory locations of the gpr registers so we can + // load them. + regs[a].location = (u8*)&mips_->r[a]; } - for (int i = 0; i < 32; i++) + for(int a = 0; a < NUMMIPSREG; ++a) { - regs[i].location = GetDefaultLocation(i); - regs[i].away = false; + ArmCRegs[a].MIPSReg = 33; + ArmCRegs[a].Reg = PPCRegs[a]; + ArmCRegs[a].LastLoad = 0; } - - // todo: sort to find the most popular regs - /* - int maxPreload = 2; - for (int i = 0; i < 32; i++) + for(int a = 0; a < NUMARMREG; ++a) { - if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2) - { - LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false); - maxPreload--; - if (!maxPreload) - break; - } - }*/ - //Find top regs - preload them (load bursts ain't bad) - //But only preload IF written OR reads >= 3 -} - -// these are powerpc reg indices -void RegCache::Lock(int p1, int p2, int p3, int p4) -{ - locks[p1] = true; - if (p2 != 0xFF) locks[p2] = true; - if (p3 != 0xFF) locks[p3] = true; - if (p4 != 0xFF) locks[p4] = true; -} - -// these are x64 reg indices -void RegCache::LockX(int x1, int x2, int x3, int x4) -{ - if (xlocks[x1]) { - PanicAlert("RegCache: x %i already locked!", x1); + ArmRegs[a].Reg = Regs[a]; + ArmRegs[a].free = true; } - xlocks[x1] = true; - if (x2 != 0xFF) xlocks[x2] = true; - if (x3 != 0xFF) xlocks[x3] = true; - if (x4 != 0xFF) xlocks[x4] = true; } -bool RegCache::IsFreeX(int xreg) const +void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { - return xregs[xreg].free && !xlocks[xreg]; -} - -void RegCache::UnlockAll() -{ - for (int i = 0; i < 32; i++) - locks[i] = false; -} - -void RegCache::UnlockAllX() -{ - for (int i = 0; i < NUMARMREGS; i++) - xlocks[i] = false; -} - -ARMReg RegCache::GetFreeXReg() -{ - int aCount; - const int *aOrder = GetAllocationOrder(aCount); - for (int i = 0; i < aCount; i++) + for(int a = 0; a < NUMMIPSREG; ++a) { - ARMReg xr = (ARMReg)aOrder[i]; - if (!xlocks[xr] && xregs[xr].free) - { - return (ARMReg)xr; - } + ArmCRegs[a].MIPSReg = 33; + ArmCRegs[a].LastLoad = 0; } - //Okay, not found :( Force grab one +} - //TODO - add a pass to grab xregs whose ppcreg is not used in the next 3 instructions - for (int i = 0; i < aCount; i++) +ARMReg *ArmRegCache::GetMIPSAllocationOrder(int &count) +{ + // This will return us the allocation order of the registers we can use on + // the MIPS side. + static ARMReg allocationOrder[] = { - ARMReg xr = (ARMReg)aOrder[i]; - if (xlocks[xr]) - continue; - int preg = xregs[xr].ppcReg; - if (!locks[preg]) - { - StoreFromRegister(preg); - return xr; - } - } - //Still no dice? Die! - _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); - return (ARMReg) -1; + R0, R1, R2, R3, R4, R5, R6, R7, R8, R9 + }; + count = sizeof(allocationOrder) / sizeof(const int); + return allocationOrder; } - -void RegCache::SaveState() +ARMReg *ArmRegCache::GetAllocationOrder(int &count) { - memcpy(saved_locks, locks, sizeof(locks)); - memcpy(saved_xlocks, xlocks, sizeof(xlocks)); - memcpy(saved_regs, regs, sizeof(regs)); - memcpy(saved_xregs, xregs, sizeof(xregs)); -} - -void RegCache::LoadState() -{ - memcpy(xlocks, saved_xlocks, sizeof(xlocks)); - memcpy(locks, saved_locks, sizeof(locks)); - memcpy(regs, saved_regs, sizeof(regs)); - memcpy(xregs, saved_xregs, sizeof(xregs)); -} - -void RegCache::FlushR(ARMReg reg) -{ - if (reg >= NUMARMREGS) - PanicAlert("Flushing non existent reg"); - if (!xregs[reg].free) + // This will return us the allocation order of the registers we can use on + // the host side. + static ARMReg allocationOrder[] = { - StoreFromRegister(xregs[reg].ppcReg); - } -} - -int RegCache::SanityCheck() const -{ - for (int i = 0; i < 32; i++) { - if (regs[i].away) { - if (regs[i].location.IsSimpleReg()) { - ARMReg simple = regs[i].location.GetSimpleReg(); - if (xlocks[simple]) - return 1; - if (xregs[simple].ppcReg != i) - return 2; - } - else if (regs[i].location.IsImm()) - return 3; - } - } - return 0; -} - -void RegCache::DiscardRegContentsIfCached(int preg) -{ - if (regs[preg].away && regs[preg].location.IsSimpleReg()) - { - ARMReg xr = regs[preg].location.GetSimpleReg(); - xregs[xr].free = true; - xregs[xr].dirty = false; - xregs[xr].ppcReg = -1; - regs[preg].away = false; - regs[preg].location = GetDefaultLocation(preg); - } -} - - -void GPRRegCache::SetImmediate32(int preg, u32 immValue) -{ - //if (regs[preg].away == true && regs[preg].location.IsImm() && regs[preg].location.offset == immValue) - // return; - DiscardRegContentsIfCached(preg); - regs[preg].away = true; - regs[preg].location.SetImm32(immValue); -} - -void GPRRegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) -{ - RegCache::Start(mips, stats); -} - -void FPURegCache::Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) -{ - RegCache::Start(mips, stats); -} - -const int *GPRRegCache::GetAllocationOrder(int &count) -{ + R14, R12, R11, R10 + }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; } -const int *FPURegCache::GetAllocationOrder(int &count) +ARMReg ArmRegCache::GetReg(bool AutoLock) { - static const int allocationOrder[] = + for (int a = 0; a < NUMARMREG; ++a) { - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 - }; - count = sizeof(allocationOrder) / sizeof(int); - return allocationOrder; -} - -Location GPRRegCache::GetDefaultLocation(int reg) const -{ - Location loc; - loc.SetM(&mips->r[reg]); - return loc; -} - -Location FPURegCache::GetDefaultLocation(int reg) const -{ - Location loc; - loc.SetM(&mips->f[reg]); - return loc; -} - -void RegCache::KillImmediate(int preg, bool doLoad, bool makeDirty) -{ - if (regs[preg].away) - { - if (regs[preg].location.IsImm()) - BindToRegister(preg, doLoad, makeDirty); - else if (regs[preg].location.IsSimpleReg()) - xregs[RX(preg)].dirty |= makeDirty; - } -} - -void GPRRegCache::BindToRegister(int i, bool doLoad, bool makeDirty) -{ - if (!regs[i].away && regs[i].location.IsImm()) - PanicAlert("Bad immediate"); - - if (!regs[i].away || (regs[i].away && regs[i].location.IsImm())) - { - ARMReg xr = GetFreeXReg(); - if (xregs[xr].dirty) PanicAlert("Xreg already dirty"); - if (xlocks[xr]) PanicAlert("GetFreeXReg returned locked register"); - xregs[xr].free = false; - xregs[xr].ppcReg = i; - xregs[xr].dirty = makeDirty || regs[i].location.IsImm(); - Location newloc; - newloc.SetReg(xr); - - //if (doLoad) - // emit->MOV(32, newloc, regs[i].location); - for (int j = 0; j < 32; j++) + if(ArmRegs[a].free) { - if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr) - { - PanicAlert("bad"); - } + // Alright, this one is free + if (AutoLock) + ArmRegs[a].free = false; + return ArmRegs[a].Reg; } - regs[i].away = true; - regs[i].location = newloc; } - else + // Uh Oh, we have all them locked.... + _assert_msg_(JIT, false, "All available registers are locked dumb dumb"); +} +void ArmRegCache::Lock(ARMReg Reg) +{ + for (int RegNum = 0; RegNum < NUMARMREG; ++RegNum) { - // reg location must be simplereg; memory locations - // and immediates are taken care of above. - xregs[RX(i)].dirty |= makeDirty; + if(ArmRegs[RegNum].Reg == Reg) + { + _assert_msg_(JIT, ArmRegs[RegNum].free, "This register is already locked"); + ArmRegs[RegNum].free = false; + } } - if (xlocks[RX(i)]) { - PanicAlert("Seriously WTF, this reg should have been flushed"); + _assert_msg_(JIT, false, "Register %d can't be used with lock", Reg); +} +void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3) +{ + for (int RegNum = 0; RegNum < NUMARMREG; ++RegNum) + { + if(ArmRegs[RegNum].Reg == R0) + { + _assert_msg_(JIT, !ArmRegs[RegNum].free, "This register is already unlocked"); + ArmRegs[RegNum].free = true; + } + if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true; + if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true; + if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true; } } -void GPRRegCache::StoreFromRegister(int i) +ARMReg ArmRegCache::R(int preg) { - if (regs[i].away) - { - bool doStore; - if (regs[i].location.IsSimpleReg()) + u32 HighestUsed = 0; + u8 Num = 0; + for (int a = 0; a < NUMMIPSREG; ++a){ + ++ArmCRegs[a].LastLoad; + if (ArmCRegs[a].LastLoad > HighestUsed) { - ARMReg xr = RX(i); - xregs[xr].free = true; - xregs[xr].ppcReg = -1; - doStore = xregs[xr].dirty; - xregs[xr].dirty = false; + HighestUsed = ArmCRegs[a].LastLoad; + Num = a; } - else - { - //must be immediate - do nothing - doStore = true; - } - Location newLoc = GetDefaultLocation(i); - if (doStore) - ; - // emit->MOV(32, newLoc, regs[i].location); - regs[i].location = newLoc; - regs[i].away = false; } + // Check if already Loaded + for (int a = 0; a < NUMMIPSREG; ++a) { + if (ArmCRegs[a].MIPSReg == preg) + { + ArmCRegs[a].LastLoad = 0; + return ArmCRegs[a].Reg; + } + } + // Check if we have a free register + for (u8 a = 0; a < NUMMIPSREG; ++a) + if (ArmCRegs[a].MIPSReg == 33) + { + emit->ARMABI_MOVI2R(ArmCRegs[a].Reg, (u32)&mips_->r); + emit->LDR(ArmCRegs[a].Reg, ArmCRegs[a].Reg, preg * 4); + ArmCRegs[a].MIPSReg = preg; + ArmCRegs[a].LastLoad = 0; + return ArmCRegs[a].Reg; + } + // Alright, we couldn't get a free space, dump that least used register + // Note that this is incredibly dangerous if references to the register + // are still floating around out there! + ARMReg rA = GetReg(false); + emit->ARMABI_MOVI2R(rA, (u32)&mips_->r); + emit->STR(rA, ArmCRegs[Num].Reg, ArmCRegs[Num].MIPSReg * 4); + emit->LDR(ArmCRegs[Num].Reg, rA, preg * 4); + ArmCRegs[Num].MIPSReg = preg; + ArmCRegs[Num].LastLoad = 0; + return ArmCRegs[Num].Reg; } -void FPURegCache::BindToRegister(int i, bool doLoad, bool makeDirty) +void ArmRegCache::Flush() { - _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm"); - if (!regs[i].away) - { - // Reg is at home in the memory register file. Let's pull it out. - ARMReg xr = GetFreeXReg(); - _assert_msg_(DYNA_REC, xr < NUMARMREGS, "WTF - load - invalid reg"); - xregs[xr].ppcReg = i; - xregs[xr].free = false; - xregs[xr].dirty = makeDirty; - Location newloc; - newloc.SetReg(xr); - if (doLoad) - { - //if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF)) - //{ - // PanicAlert("WARNING - misaligned fp register location %i", i); - //} - //emit->MOVAPD(xr, regs[i].location); - } - regs[i].location = newloc; - regs[i].away = true; - } else { - // There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary. - xregs[RX(i)].dirty |= makeDirty; - } -} + // Maybe we should keep this pointer around permanently? + emit->MOVW(R14, (u32)&mips_->r); + emit->MOVT(R14, (u32)&mips_->r, true); -void FPURegCache::StoreFromRegister(int i) -{ - _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm"); - if (regs[i].away) - { - ARMReg xr = regs[i].location.GetSimpleReg(); - _assert_msg_(DYNA_REC, xr < NUMARMREGS, "WTF - store - invalid reg"); - xregs[xr].free = true; - xregs[xr].dirty = false; - xregs[xr].ppcReg = -1; - Location newLoc = GetDefaultLocation(i); - // emit->MOVAPD(newLoc, xr); - regs[i].location = newLoc; - regs[i].away = false; - } - else - { - // _assert_msg_(DYNA_REC,0,"already stored"); - } -} - -void RegCache::Flush(FlushMode mode) -{ - for (int i = 0; i < NUMARMREGS; i++) { - if (xlocks[i]) - PanicAlert("Someone forgot to unlock X64 reg %i.", i); - } - for (int i = 0; i < 32; i++) - { - if (locks[i]) + for (int a = 0; a < NUMMIPSREG; ++a) { + if (ArmCRegs[a].MIPSReg != 33) { - PanicAlert("Somebody forgot to unlock PPC reg %i.", i); - } - if (regs[i].away) - { - if (regs[i].location.IsSimpleReg()) - { - ARMReg xr = RX(i); - StoreFromRegister(i); - xregs[xr].dirty = false; - } - else if (regs[i].location.IsImm()) - { - StoreFromRegister(i); - } - else - { - _assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i PC: %08x", i, mips->pc); - } + emit->STR(R14, ArmCRegs[a].Reg, ArmCRegs[a].MIPSReg * 4); + ArmCRegs[a].MIPSReg = 33; + ArmCRegs[a].LastLoad = 0; } } } + diff --git a/Core/MIPS/ARM/RegCache.h b/Core/MIPS/ARM/RegCache.h index afab156b52..ab5d85c0ca 100644 --- a/Core/MIPS/ARM/RegCache.h +++ b/Core/MIPS/ARM/RegCache.h @@ -2,11 +2,11 @@ // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0 or later versions. +// the Free Software Foundation, version 2.0. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. @@ -15,156 +15,82 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#pragma once +#ifndef _JITARMREGCACHE_H +#define _JITARMREGCACHE_H +#include "ArmEmitter.h" +#include "../MIPS.h" #include "../MIPSAnalyst.h" -#include +#include "ArmABI.h" using namespace ArmGen; -enum FlushMode -{ - FLUSH_ALL -}; -enum GrabMode -{ - M_READ = 1, - M_WRITE = 2, - M_READWRITE = 3, -}; - -enum Loc { - LOC_IMM, - LOC_REG, - LOC_MEM -}; - -struct Location -{ - Loc loc; - bool IsSimpleReg() const {return loc == LOC_REG;} - ARMReg GetSimpleReg() const {return reg;} - bool IsImm() const { return loc == LOC_IMM; } - void SetImm32(u32 i) {loc = LOC_IMM; imm = i;} - void SetM(void *p) {loc = LOC_MEM; ptr = (u32 *)p;} - void SetReg(ARMReg r) {loc = LOC_REG; reg = r;} - - union { - u32 *ptr; - ARMReg reg; - u32 imm; - }; -}; +// This ARM Register cache actually pre loads the most used registers before +// the block to increase speed since every memory load requires two +// instructions to load it. We are going to use R0-RMAX as registers for the +// use of MIPS Registers. +// Allocation order as follows +#define ARMREGS 16 +// Allocate R0 to R9 for MIPS first. +// For General registers on the host side, start with R14 and go down as we go +// R13 is reserved for our stack pointer, don't ever use that. Unless you save +// it +// So we have R14, R12, R11, R10 to work with instructions struct MIPSCachedReg { - Location location; - bool away; // value not in source register + const u8 *location; }; -struct ARMCachedReg +struct JRCPPC { - int ppcReg; - bool dirty; + u32 MIPSReg; // Tied to which MIPS Register + ARMReg Reg; // Tied to which ARM Register + u32 LastLoad; +}; +struct JRCReg +{ + ARMReg Reg; // Which reg this is. bool free; }; - -typedef int XReg; -typedef int PReg; - -#define NUMARMREGS 15 - -class RegCache +class ArmRegCache { private: - bool locks[32]; - bool saved_locks[32]; - bool saved_xlocks[NUMARMREGS]; + MIPSCachedReg regs[32]; + JRCPPC ArmCRegs[ARMREGS]; + JRCReg ArmRegs[ARMREGS]; // Four registers remaining + + int NUMMIPSREG; // + LO, HI, ... + int NUMARMREG; + + ARMReg *GetAllocationOrder(int &count); + ARMReg *GetMIPSAllocationOrder(int &count); + + MIPSState *mips_; protected: - bool xlocks[NUMARMREGS]; - MIPSCachedReg regs[32]; - ARMCachedReg xregs[NUMARMREGS]; - - MIPSCachedReg saved_regs[32]; - ARMCachedReg saved_xregs[NUMARMREGS]; - - virtual const int *GetAllocationOrder(int &count) = 0; - ARMXEmitter *emit; public: - MIPSState *mips; - RegCache(); + ArmRegCache(MIPSState *mips); + ~ArmRegCache() {} - virtual ~RegCache() {} - virtual void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats) = 0; + void Init(ARMXEmitter *emitter); + void Start(MIPSAnalyst::AnalysisResults &stats); - void DiscardRegContentsIfCached(int preg); void SetEmitter(ARMXEmitter *emitter) {emit = emitter;} - void FlushR(ARMReg reg); - void FlushR(ARMReg reg, ARMReg reg2) {FlushR(reg); FlushR(reg2);} - void FlushLockX(ARMReg reg) { - FlushR(reg); - LockX(reg); - } - void FlushLockX(ARMReg reg1, ARMReg reg2) { - FlushR(reg1); FlushR(reg2); - LockX(reg1); LockX(reg2); - } - virtual void Flush(FlushMode mode); - // virtual void Flush(PPCAnalyst::CodeOp *op) {Flush(FLUSH_ALL);} - int SanityCheck() const; - void KillImmediate(int preg, bool doLoad, bool makeDirty); + // TODO: Add a way to lock MIPS registers so they aren't kicked out when you don't expect it. - //TODO - instead of doload, use "read", "write" - //read only will not set dirty flag - virtual void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true) = 0; - virtual void StoreFromRegister(int preg) = 0; + ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use. + void Lock(ARMReg R0); + void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 = INVALID_REG); + void Flush(); + ARMReg R(int preg); // Returns a cached register - const Location &R(int preg) const {return regs[preg].location;} - ARMReg RX(int preg) const - { - if (regs[preg].away && regs[preg].location.IsSimpleReg()) - return regs[preg].location.GetSimpleReg(); - PanicAlert("Not so simple - %i", preg); - return (ARMReg)-1; - } - virtual Location GetDefaultLocation(int reg) const = 0; - - // Register locking. A locked registers will not be spilled when trying to find a new free register. - void Lock(int p1, int p2=0xff, int p3=0xff, int p4=0xff); - void LockX(int x1, int x2=0xff, int x3=0xff, int x4=0xff); - void UnlockAll(); - void UnlockAllX(); - - bool IsFreeX(int xreg) const; - - ARMReg GetFreeXReg(); - - void SaveState(); - void LoadState(); -}; - -class GPRRegCache : public RegCache -{ -public: - void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats); - void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true); - void StoreFromRegister(int preg); - Location GetDefaultLocation(int reg) const; - const int *GetAllocationOrder(int &count); - void SetImmediate32(int preg, u32 immValue); }; -class FPURegCache : public RegCache -{ -public: - void Start(MIPSState *mips, MIPSAnalyst::AnalysisResults &stats); - void BindToRegister(int preg, bool doLoad = true, bool makeDirty = true); - void StoreFromRegister(int preg); - const int *GetAllocationOrder(int &count); - Location GetDefaultLocation(int reg) const; -}; + + +#endif diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 8809408ade..0f86ef108e 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -212,6 +212,12 @@ + + true + true + true + true + true true @@ -290,6 +296,7 @@ + @@ -322,6 +329,7 @@ + @@ -359,4 +367,4 @@ - + \ No newline at end of file diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index 5786358b73..ba3dce5c47 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -104,6 +104,9 @@ + + Android + @@ -182,6 +185,9 @@ Windows\Input + + Android + @@ -203,6 +209,7 @@ Android + diff --git a/android/jni/ARMEmitterTest.h b/android/jni/ARMEmitterTest.h new file mode 100644 index 0000000000..4d2100ce61 --- /dev/null +++ b/android/jni/ARMEmitterTest.h @@ -0,0 +1,5 @@ +#pragma once + +// Just a test of the ARM emitter, playing around with running some code without having the whole emu around. + +void ArmEmitterTest(); \ No newline at end of file diff --git a/android/jni/Android.mk b/android/jni/Android.mk index a98a774767..8f61ef704a 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -46,6 +46,7 @@ LOCAL_SRC_FILES := \ MenuScreens.cpp \ UIShader.cpp \ GamepadEmu.cpp \ + ArmEmitterTest.cpp \ ui_atlas.cpp \ $(SRC)/native/android/app-android.cpp \ $(SRC)/ext/libkirk/AES.c \ @@ -55,6 +56,7 @@ LOCAL_SRC_FILES := \ $(SRC)/ext/libkirk/kirk_engine.c \ $(SRC)/Common/ArmABI.cpp \ $(SRC)/Common/ArmEmitter.cpp \ + $(SRC)/Common/ArmCPUDetect.cpp \ $(SRC)/Common/LogManager.cpp \ $(SRC)/Common/MemArena.cpp \ $(SRC)/Common/MemoryUtil.cpp \ diff --git a/android/jni/ArmEmitterTest.cpp b/android/jni/ArmEmitterTest.cpp new file mode 100644 index 0000000000..0bdf26af19 --- /dev/null +++ b/android/jni/ArmEmitterTest.cpp @@ -0,0 +1,75 @@ +#include "base/logging.h" +#include "ARMEmitterTest.h" + +#include "Common/ArmABI.h" +#include "Common/ArmEmitter.h" + +static bool functionWasCalled; + +using namespace ArmGen; + +class TestCode : public ArmGen::ARMXCodeBlock { +public: + TestCode(); + void Generate(); + const u8 *testCodePtr; + const u8 *testCodePtr2; +}; + +TestCode::TestCode() +{ + AllocCodeSpace(0x10000); +} + + +u32 TestLeaf(u32 a, u32 b, u32 c) +{ + ILOG("TestLeaf: %08x %08x %08x\n", a, b, c); + return 0xFF; +} + +void TestLeaf2(u32 a) +{ + ILOG("TestLeaf2 %08x\n"); +} + + +void TestCode::Generate() +{ + testCodePtr = this->GetCodePtr(); + // Sonic1 commented that R11 is the frame pointer in debug mode, whatever "debug mode" means. + PUSH(2, R11, _LR); + ARMABI_MOVI2R(R0, 0x13371338); + ARMABI_MOVI2R(R1, 0x1337); + ARMABI_CallFunction((void*)&TestLeaf2); + + //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x1, 0x100, 0x1337); + //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x2, 0x100, 0x31337); + //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x3, 0x100, 0x1337); + POP(2, R11, _PC); // Yup, this is how you return. + + testCodePtr2 = this->GetCodePtr(); + PUSH(2, R11, _LR); + ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); + ARMABI_CallFunction((void*)&TestLeaf2); + ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); + POP(2, R11, _PC); +} + + +void CallPtr(const void *ptr) +{ + ((void(*)())ptr)(); +} + + + +void ArmEmitterTest() +{ + TestCode gen; + gen.ReserveCodeSpace(0x4000); + gen.Generate(); + + CallPtr(gen.testCodePtr); + ILOG("ARM emitter test 1 passed!"); +} \ No newline at end of file diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 66d27ae616..0f13678908 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -47,6 +47,8 @@ #include "MenuScreens.h" #include "UIShader.h" +#include "ARMEmitterTest.h" + Texture *uiTexture; ScreenManager *screenManager; @@ -145,6 +147,8 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo *app_nice_name = "PPSSPP"; *app_dir_name = "ppsspp"; *landscape = true; + + ArmEmitterTest(); } void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID) From 863bc863c9a5edf5aa076a30a10c37f3df58ccc9 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 24 Nov 2012 17:51:10 +0100 Subject: [PATCH 02/49] Some more arm work --- Common/ArmEmitter.cpp | 8 ++++---- Core/MIPS/ARM/CompBranch.cpp | 6 +++--- android/jni/ArmEmitterTest.cpp | 11 +++++++++-- android/jni/MenuScreens.cpp | 7 ++++++- android/src/org/ppsspp/ppsspp/PpssppActivity.java | 2 +- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 5de8fb6d7b..f1b9f322e7 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -362,13 +362,13 @@ void ARMXEmitter::REV (ARMReg dest, ARMReg src ) Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); } -void ARMXEmitter::_MSR (bool nzcvq, bool g, Operand2 op2) +void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, Operand2 op2) { - Write32(condition | (0x320F << 12) | (nzcvq << 19) | (g << 18) | op2.Imm12Mod()); + Write32(condition | (0x320F << 12) | (write_nzcvq << 19) | (write_g << 18) | op2.Imm12Mod()); } -void ARMXEmitter::_MSR (bool nzcvq, bool g, ARMReg src) +void ARMXEmitter::_MSR (bool write_nzcvq, bool write_g, ARMReg src) { - Write32(condition | (0x120F << 12) | (nzcvq << 19) | (g << 18) | src); + Write32(condition | (0x120F << 12) | (write_nzcvq << 19) | (write_g << 18) | src); } void ARMXEmitter::MRS (ARMReg dest) { diff --git a/Core/MIPS/ARM/CompBranch.cpp b/Core/MIPS/ARM/CompBranch.cpp index 7c110ff4cd..cdcb257f4e 100644 --- a/Core/MIPS/ARM/CompBranch.cpp +++ b/Core/MIPS/ARM/CompBranch.cpp @@ -81,7 +81,7 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) CompileAt(js.compilerPC + 4); FlushAll(); POP(1, R0); - _MSR(false, false, R0); // Restore flags register + _MSR(true, false, R0); // Restore flags register ptr = B_CC(cc); } else @@ -133,7 +133,7 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) FlushAll(); POP(1, R0); - _MSR(false, false, R0); // Restore flags register + _MSR(true, false, R0); // Restore flags register ptr = B_CC(cc); } else @@ -224,7 +224,7 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) // POPF(); // restore flag! POP(1, R0); - _MSR(false, false, R0); // Restore flags register + _MSR(true, false, R0); // Restore flags register ptr = B_CC(cc); } else diff --git a/android/jni/ArmEmitterTest.cpp b/android/jni/ArmEmitterTest.cpp index 0bdf26af19..9a7215046c 100644 --- a/android/jni/ArmEmitterTest.cpp +++ b/android/jni/ArmEmitterTest.cpp @@ -40,8 +40,15 @@ void TestCode::Generate() // Sonic1 commented that R11 is the frame pointer in debug mode, whatever "debug mode" means. PUSH(2, R11, _LR); ARMABI_MOVI2R(R0, 0x13371338); - ARMABI_MOVI2R(R1, 0x1337); - ARMABI_CallFunction((void*)&TestLeaf2); + AND(R1, R0, Operand2(0xFC, 4)); + BIC(R0, R0, Operand2(0xFC, 4)); + CMP(R1, Operand2(0x10, 4)); + SetCC(CC_EQ); + MOV(R2, Operand2(0x99, 0)); + SetCC(CC_NEQ); + MOV(R2, Operand2(0xFF, 0)); + SetCC(); + ARMABI_CallFunction((void*)&TestLeaf); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x1, 0x100, 0x1337); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x2, 0x100, 0x31337); diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index b885bc3acb..0671602af4 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -245,8 +245,13 @@ void SettingsScreen::render() { bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER; UICheckBox(GEN_ID, x, y += 50, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt); - ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x += 50, 0xcFFFFFFF, ALIGN_LEFT); g_Config.iCpuCore = useFastInt ? CPU_FASTINTERPRETER : CPU_INTERPRETER; + + bool useJit = g_Config.iCpuCore == CPU_JIT; + UICheckBox(GEN_ID, x, y += 50, "Use JIT (very crashy)", ALIGN_TOPLEFT, &useJit); + if (useJit) { + g_Config.iCpuCore = CPU_JIT; + } // UICheckBox(GEN_ID, x, y += 50, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer); if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres-10), LARGE_BUTTON_WIDTH, "Back", ALIGN_RIGHT | ALIGN_BOTTOM)) { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index bd287150fa..be46ac6038 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity { { return false; } -} \ No newline at end of file +} \ No newline at end of file From 62908325a0b804f1d623767ea91f3e2c1e4d374c Mon Sep 17 00:00:00 2001 From: Sacha Date: Wed, 5 Dec 2012 18:09:14 +1000 Subject: [PATCH 03/49] Make the armjit branch compile on Blackberry, compatible with iOS. Still crashes on ArmEmitterTest(). --- CMakeLists.txt | 3 +- Common/ArmCPUDetect.cpp | 33 +++++++++++++++++-- Common/ArmEmitter.cpp | 2 +- android/jni/ArmEmitterTest.cpp | 4 +-- .../{ARMEmitterTest.h => ArmEmitterTest.h} | 0 android/jni/NativeApp.cpp | 2 +- 6 files changed, 37 insertions(+), 7 deletions(-) rename android/jni/{ARMEmitterTest.h => ArmEmitterTest.h} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index e8b78fedc6..05256d63f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -867,7 +867,8 @@ set(NativeAppSource android/jni/MenuScreens.cpp android/jni/GamepadEmu.cpp android/jni/UIShader.cpp - android/jni/ui_atlas.cpp) + android/jni/ui_atlas.cpp + android/jni/ArmEmitterTest.cpp) set(AndroidAssets android/assets/ui_atlas.zim android/assets/ppge_atlas.zim) diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp index 1e01732e23..453c580097 100644 --- a/Common/ArmCPUDetect.cpp +++ b/Common/ArmCPUDetect.cpp @@ -19,6 +19,8 @@ #include "CPUDetect.h" #include "StringUtil.h" +// Only Linux platforms have /proc/cpuinfo +#if !defined(BLACKBERRY) && !defined(IOS) const char procfile[] = "/proc/cpuinfo"; char *GetCPUString() @@ -68,8 +70,12 @@ bool CheckCPUFeature(const char *feature) } return false; } +#endif int GetCoreCount() { +#if defined(BLACKBERRY) || defined(IOS) + return 2; +#else const char marker[] = "processor\t: "; int cores = 0; char buf[1024]; @@ -86,6 +92,7 @@ int GetCoreCount() ++cores; } return cores; +#endif } CPUInfo cpu_info; @@ -106,8 +113,10 @@ void CPUInfo::Detect() vendor = VENDOR_ARM; // Get the information about the CPU + num_cores = GetCoreCount(); +// Hardcode this for now +#if !defined(BLACKBERRY) && !defined(IOS) strncpy(cpu_string, GetCPUString(), sizeof(cpu_string)); - num_cores = GetCoreCount(); bSwp = CheckCPUFeature("swp"); bHalf = CheckCPUFeature("half"); bThumb = CheckCPUFeature("thumb"); @@ -124,17 +133,37 @@ void CPUInfo::Detect() // These two are ARMv8 specific. bFP = CheckCPUFeature("fp"); bASIMD = CheckCPUFeature("asimd"); +#else + bSwp = true; + bHalf = true; + bThumb = false; + bFastMult = true; + bVFP = true; + bEDSP = true; + bThumbEE = true; + bNEON = true; + bVFPv3 = true; + bTLS = true; + bVFPv4 = false; + bIDIVa = false; + bIDIVt = false; + bFP = false; + bASIMD = false; +#endif } // Turn the cpu info into a string we can show std::string CPUInfo::Summarize() { std::string sum; +#if defined(BLACKBERRY) || defined(IOS) + sum = StringFromFormat("%i cores", num_cores); +#else if (num_cores == 1) sum = StringFromFormat("%s, %i core", cpu_string, num_cores); else sum = StringFromFormat("%s, %i cores", cpu_string, num_cores); - +#endif if (bSwp) sum += ", SWP"; if (bHalf) sum += ", Half"; if (bThumb) sum += ", Thumb"; diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 5de8fb6d7b..268faa7037 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -61,7 +61,7 @@ const u8 *ARMXEmitter::AlignCodePage() void ARMXEmitter::Flush() { - __clear_cache (startcode, code); + __builtin___clear_cache (startcode, code); SLEEP(0); } void ARMXEmitter::SetCC(CCFlags cond) diff --git a/android/jni/ArmEmitterTest.cpp b/android/jni/ArmEmitterTest.cpp index 0bdf26af19..0929c17c37 100644 --- a/android/jni/ArmEmitterTest.cpp +++ b/android/jni/ArmEmitterTest.cpp @@ -1,5 +1,5 @@ #include "base/logging.h" -#include "ARMEmitterTest.h" +#include "ArmEmitterTest.h" #include "Common/ArmABI.h" #include "Common/ArmEmitter.h" @@ -72,4 +72,4 @@ void ArmEmitterTest() CallPtr(gen.testCodePtr); ILOG("ARM emitter test 1 passed!"); -} \ No newline at end of file +} diff --git a/android/jni/ARMEmitterTest.h b/android/jni/ArmEmitterTest.h similarity index 100% rename from android/jni/ARMEmitterTest.h rename to android/jni/ArmEmitterTest.h diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index c6399af9f2..82f5532158 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -48,7 +48,7 @@ #include "MenuScreens.h" #include "UIShader.h" -#include "ARMEmitterTest.h" +#include "ArmEmitterTest.h" Texture *uiTexture; From fcf4cfa767429c9f14aea903994345f667bafc5d Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 7 Jan 2013 13:11:33 +0100 Subject: [PATCH 04/49] Rename ThunkARM.cpp->ArmThunk.cpp --- CMakeLists.txt | 2 +- Common/{ThunkARM.cpp => ArmThunk.cpp} | 0 android/jni/Android.mk | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename Common/{ThunkARM.cpp => ArmThunk.cpp} (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3a61dbd3ef..860e4e8b72 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,7 +170,7 @@ if(ARM) Common/ArmCPUDetect.cpp Common/ArmEmitter.h Common/ArmEmitter.cpp - Common/ThunkARM.cpp) + Common/ArmThunk.cpp) elseif(X86) set(CommonExtra ${CommonExtra} Common/ABI.cpp diff --git a/Common/ThunkARM.cpp b/Common/ArmThunk.cpp similarity index 100% rename from Common/ThunkARM.cpp rename to Common/ArmThunk.cpp diff --git a/android/jni/Android.mk b/android/jni/Android.mk index f38cdd6f06..0b99805fe0 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -59,6 +59,7 @@ LOCAL_SRC_FILES := \ $(SRC)/Common/ArmABI.cpp \ $(SRC)/Common/ArmEmitter.cpp \ $(SRC)/Common/ArmCPUDetect.cpp \ + $(SRC)/Common/ArmThunk.cpp \ $(SRC)/Common/LogManager.cpp \ $(SRC)/Common/MemArena.cpp \ $(SRC)/Common/MemoryUtil.cpp \ @@ -68,7 +69,6 @@ LOCAL_SRC_FILES := \ $(SRC)/Common/StringUtil.cpp \ $(SRC)/Common/Thread.cpp \ $(SRC)/Common/Timer.cpp \ - $(SRC)/Common/ThunkARM.cpp \ $(SRC)/Common/Misc.cpp \ $(SRC)/Common/MathUtil.cpp \ $(SRC)/GPU/Math3D.cpp \ From 2f69250fc0fd5f3d7762be9497cb06f05d965433 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 7 Jan 2013 13:42:05 +0100 Subject: [PATCH 05/49] Store fcr0 and fcr31 in context (why was this commented out?) --- Core/HLE/sceKernelThread.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index 5beb86cee8..e872e20764 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -915,9 +915,9 @@ void __KernelSaveContext(ThreadContext *ctx) ctx->hi = currentMIPS->hi; ctx->lo = currentMIPS->lo; ctx->pc = currentMIPS->pc; + ctx->fcr0 = currentMIPS->fcr0; + ctx->fcr31 = currentMIPS->fcr31; ctx->fpcond = currentMIPS->fpcond; - // ctx->fcr0 = currentMIPS->fcr0; - // ctx->fcr31 = currentMIPS->fcr31; // TODO: Make VFPU saving optional/delayed, only necessary between VFPU-attr-marked threads } @@ -941,9 +941,9 @@ void __KernelLoadContext(ThreadContext *ctx) currentMIPS->hi = ctx->hi; currentMIPS->lo = ctx->lo; currentMIPS->pc = ctx->pc; + currentMIPS->fcr0 = ctx->fcr0; + currentMIPS->fcr31 = ctx->fcr31; currentMIPS->fpcond = ctx->fpcond; - // currentMIPS->fcr0 = ctx->fcr0; - // currentMIPS->fcr31 = ctx->fcr31; } u32 __KernelResumeThreadFromWait(SceUID threadID) From a2ff416534eb2ab50654fb9c1728635afe618a01 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 7 Jan 2013 22:33:09 +0100 Subject: [PATCH 06/49] Rename files. Rewrite ArmRegCache from scratch. --- Core/Core.vcxproj | 32 +-- Core/Core.vcxproj.filters | 77 +++--- Core/MIPS/ARM/{Asm.cpp => ArmAsm.cpp} | 89 ++----- Core/MIPS/ARM/{Asm.h => ArmAsm.h} | 20 +- Core/MIPS/ARM/{CompALU.cpp => ArmCompALU.cpp} | 6 +- .../ARM/{CompBranch.cpp => ArmCompBranch.cpp} | 10 +- Core/MIPS/ARM/{CompFPU.cpp => ArmCompFPU.cpp} | 4 +- ...CompLoadStore.cpp => ArmCompLoadStore.cpp} | 4 +- .../ARM/{CompVFPU.cpp => ArmCompVFPU.cpp} | 4 +- Core/MIPS/ARM/{Jit.cpp => ArmJit.cpp} | 55 ++--- Core/MIPS/ARM/{Jit.h => ArmJit.h} | 29 +-- .../ARM/{JitCache.cpp => ArmJitCache.cpp} | 73 +++--- Core/MIPS/ARM/{JitCache.h => ArmJitCache.h} | 14 +- Core/MIPS/ARM/ArmRegCache.cpp | 189 +++++++++++++++ Core/MIPS/ARM/ArmRegCache.h | 112 +++++++++ Core/MIPS/ARM/RegCache.cpp | 219 ------------------ Core/MIPS/ARM/RegCache.h | 96 -------- Core/MIPS/JitCommon/JitCommon.h | 2 +- Core/MIPS/MIPS.cpp | 10 +- Core/MIPS/MIPS.h | 9 +- Core/MIPS/x86/Asm.cpp | 41 +--- Core/MIPS/x86/Asm.h | 15 +- Core/MIPS/x86/Jit.cpp | 5 - android/jni/Android.mk | 17 +- android/jni/Application.mk | 5 +- 25 files changed, 493 insertions(+), 644 deletions(-) rename Core/MIPS/ARM/{Asm.cpp => ArmAsm.cpp} (65%) rename Core/MIPS/ARM/{Asm.h => ArmAsm.h} (62%) rename Core/MIPS/ARM/{CompALU.cpp => ArmCompALU.cpp} (99%) rename Core/MIPS/ARM/{CompBranch.cpp => ArmCompBranch.cpp} (98%) rename Core/MIPS/ARM/{CompFPU.cpp => ArmCompFPU.cpp} (99%) rename Core/MIPS/ARM/{CompLoadStore.cpp => ArmCompLoadStore.cpp} (98%) rename Core/MIPS/ARM/{CompVFPU.cpp => ArmCompVFPU.cpp} (87%) rename Core/MIPS/ARM/{Jit.cpp => ArmJit.cpp} (86%) rename Core/MIPS/ARM/{Jit.h => ArmJit.h} (89%) rename Core/MIPS/ARM/{JitCache.cpp => ArmJitCache.cpp} (84%) rename Core/MIPS/ARM/{JitCache.h => ArmJitCache.h} (95%) create mode 100644 Core/MIPS/ARM/ArmRegCache.cpp create mode 100644 Core/MIPS/ARM/ArmRegCache.h delete mode 100644 Core/MIPS/ARM/RegCache.cpp delete mode 100644 Core/MIPS/ARM/RegCache.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 8e3574417c..610f37f31a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -185,49 +185,55 @@ - + true true true true - + true true true true - + true true true true - + true true true true - + true true true true - + true true true true - + true true true true - + + true + true + true + true + + true true true @@ -338,25 +344,25 @@ - + true true true true - + true true true true - + true true true true - + true true true @@ -409,4 +415,4 @@ - + \ No newline at end of file diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index c0c03cd556..03ca3df852 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -111,30 +111,6 @@ MIPS\x86 - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - MIPS\JitCommon @@ -363,6 +339,33 @@ Ext\Snappy + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + @@ -413,18 +416,6 @@ MIPS\x86 - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - - - MIPS\ARM - MIPS\JitCommon @@ -674,6 +665,18 @@ Ext\Snappy + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + + + MIPS\ARM + @@ -681,4 +684,4 @@ - + \ No newline at end of file diff --git a/Core/MIPS/ARM/Asm.cpp b/Core/MIPS/ARM/ArmAsm.cpp similarity index 65% rename from Core/MIPS/ARM/Asm.cpp rename to Core/MIPS/ARM/ArmAsm.cpp index 9072857f8a..e93e755fae 100644 --- a/Core/MIPS/ARM/Asm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -15,8 +15,8 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "ABI.h" -#include +#include "ArmEmitter.h" +#include "ArmABI.h" #include "../../MemMap.h" @@ -24,11 +24,10 @@ #include "../../CoreTiming.h" #include "MemoryUtil.h" -#include "ABI.h" -#include "Jit.h" +#include "ArmJit.h" #include "../JitCommon/JitCommon.h" #include "../../Core.h" -#include "Asm.h" +#include "ArmAsm.h" using namespace ArmGen; @@ -72,24 +71,26 @@ void Jit() void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) { enterCode = AlignCode16(); - //ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); -/* -#ifndef _M_IX86 - // Two statically allocated registers. - MOV(64, R(RBX), Imm64((u64)Memory::base)); - MOV(64, R(R15), Imm64((u64)jit->GetBlockCache()->GetCodePointers())); //It's below 2GB so 32 bits are good enough -#endif - */ PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); SetCC(CC_AL); - //ARMABI_MOVIMM32(R11, (u32)Memory::base); - //ARMABI_MOVIMM32(R10, (u32)jit->GetBlockCache()->GetCodePointers()); + + // Fixed registers, these are always kept when in Jit context. + // R13 cannot be used as it's the stack pointer. + ARMABI_MOVI2R(R11, (u32)Memory::base); + ARMABI_MOVI2R(R12, (u32)currentMIPS); + ARMABI_MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers()); outerLoop = GetCodePtr(); //ARMABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time - + + dispatcherCheckCoreState = GetCodePtr(); + + //TODO: critical + //CMP(32, M((void*)&coreState), Imm32(0)); + FixupBranch badCoreState; // = J_CC(CC_NZ, true); + dispatcher = GetCodePtr(); // The result of slice decrementation should be in flags if somebody jumped here // IMPORTANT - We jump on negative, not carry!!! @@ -127,65 +128,15 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) B(dispatcherNoCheck); // no point in special casing this SetJumpTarget(bail); - doTiming = GetCodePtr(); - testExternalExceptions = GetCodePtr(); - //TEST(32, M((void *)&mips->exceptions), Imm32(MIPSState::BREAKING_EXCEPTIONS)); - //FixupBranch noExtException = J_CC(CC_Z); - //MOV(32, R(EAX), M(&mips->pc)); - // MOV(32, M(&NPC), R(EAX)); - // ABI_CallFunction(reinterpret_cast(&MIPSState::CheckExternalExceptions)); - //SetJumpTarget(noExtException); + SetJumpTarget(badCoreState); //CMP(M((void*)&coreState), Imm8(0)); SetCC(CC_EQ); //B(outerLoop); SetCC(CC_AL); - //Landing pad for drec space - //ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); - B(_LR); + //Landing pad for drec space PUSH(8, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns - - - - GenerateCommon(); -} - -void AsmRoutineManager::GenerateCommon() -{ - /* - fifoDirectWrite8 = AlignCode4(); - GenFifoWrite(8); - fifoDirectWrite16 = AlignCode4(); - GenFifoWrite(16); - fifoDirectWrite32 = AlignCode4(); - GenFifoWrite(32); - fifoDirectWriteFloat = AlignCode4(); - GenFifoFloatWrite(); - fifoDirectWriteXmm64 = AlignCode4(); - GenFifoXmm64Write(); - - GenQuantizedLoads(); - GenQuantizedStores(); - GenQuantizedSingleStores(); - */ - - //CMPSD(R(XMM0), M(&zero), - // TODO - - // Fast write routines - special case the most common hardware write - // TODO: use this. - // Even in x86, the param values will be in the right registers. - /* - const u8 *fastMemWrite8 = AlignCode16(); - CMP(32, R(ABI_PARAM2), Imm32(0xCC008000)); - FixupBranch skip_fast_write = J_CC(CC_NE, false); - MOV(32, EAX, M(&m_gatherPipeCount)); - MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1); - ADD(32, 1, M(&m_gatherPipeCount)); - RET(); - SetJumpTarget(skip_fast_write); - CALL((void *)&Memory::Write_U8);*/ -} +} \ No newline at end of file diff --git a/Core/MIPS/ARM/Asm.h b/Core/MIPS/ARM/ArmAsm.h similarity index 62% rename from Core/MIPS/ARM/Asm.h rename to Core/MIPS/ARM/ArmAsm.h index 481e46374b..459582452e 100644 --- a/Core/MIPS/ARM/Asm.h +++ b/Core/MIPS/ARM/ArmAsm.h @@ -21,19 +21,7 @@ #include #include "../MIPS.h" -// In PPSSPP, we don't use inline assembly. Instead, we generate all machine-near -// code at runtime. In the case of fixed code like this, after writing it, we write -// protect the memory, essentially making it work just like precompiled code. - -// There are some advantages to this approach: -// 1) No need to setup an external assembler in the build. -// 2) Cross platform, as long as it's x86/x64. -// 3) Can optimize code at runtime for the specific CPU model. -// There aren't really any disadvantages other than having to maintain code emitters, -// which we have to do anyway :) -// -// To add a new asm routine, just add another const here, and add the code to Generate. -// Also, possibly increase the size of the code buffer. +// Runtime generated assembly routines, like the Dispatcher. namespace MIPSComp { @@ -65,14 +53,10 @@ public: const u8 *enterCode; const u8 *outerLoop; + const u8 *dispatcherCheckCoreState; const u8 *dispatcher; const u8 *dispatcherNoCheck; const u8 *dispatcherPcInR0; - - const u8 *fpException; - const u8 *testExceptions; - const u8 *testExternalExceptions; - const u8 *doTiming; }; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/CompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp similarity index 99% rename from Core/MIPS/ARM/CompALU.cpp rename to Core/MIPS/ARM/ArmCompALU.cpp index 5560da52e4..459e5effa9 100644 --- a/Core/MIPS/ARM/CompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -15,9 +15,9 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. -#include "Jit.h" -#include "RegCache.h" -#include +#include "ArmJit.h" +#include "ArmRegCache.h" +#include "ArmEmitter.h" using namespace MIPSAnalyst; #define _RS ((op>>21) & 0x1F) diff --git a/Core/MIPS/ARM/CompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp similarity index 98% rename from Core/MIPS/ARM/CompBranch.cpp rename to Core/MIPS/ARM/ArmCompBranch.cpp index cdcb257f4e..750589b654 100644 --- a/Core/MIPS/ARM/CompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -21,9 +21,9 @@ #include "../MIPSAnalyst.h" #include "../MIPSTables.h" -#include "Jit.h" -#include "RegCache.h" -#include "JitCache.h" +#include "ArmJit.h" +#include "ArmRegCache.h" +#include "ArmJitCache.h" #include #define _RS ((op>>21) & 0x1F) @@ -209,9 +209,9 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) } FlushAll(); - ARMABI_MOVI2R(R0, (u32)&(mips_->fpcond)); + ARMABI_MOVI2R(R0, (u32)&(mips_->fcr31)); LDR(R0, R0, Operand2(0, TYPE_IMM)); - TST(R0, Operand2(1, TYPE_IMM)); + TST(R0, Operand2(1 << 23, TYPE_IMM)); ArmGen::FixupBranch ptr; js.inDelaySlot = true; if (!likely) diff --git a/Core/MIPS/ARM/CompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp similarity index 99% rename from Core/MIPS/ARM/CompFPU.cpp rename to Core/MIPS/ARM/ArmCompFPU.cpp index 0ed608eac2..d008d2308b 100644 --- a/Core/MIPS/ARM/CompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -16,8 +16,8 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "../MIPS.h" -#include "Jit.h" -#include "RegCache.h" +#include "ArmJit.h" +#include "ArmRegCache.h" #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) diff --git a/Core/MIPS/ARM/CompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp similarity index 98% rename from Core/MIPS/ARM/CompLoadStore.cpp rename to Core/MIPS/ARM/ArmCompLoadStore.cpp index e8d8430447..fd458240f0 100644 --- a/Core/MIPS/ARM/CompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -17,8 +17,8 @@ #include "../../MemMap.h" #include "../MIPSAnalyst.h" -#include "Jit.h" -#include "RegCache.h" +#include "ArmJit.h" +#include "ArmRegCache.h" #define _RS ((op>>21) & 0x1F) diff --git a/Core/MIPS/ARM/CompVFPU.cpp b/Core/MIPS/ARM/ArmCompVFPU.cpp similarity index 87% rename from Core/MIPS/ARM/CompVFPU.cpp rename to Core/MIPS/ARM/ArmCompVFPU.cpp index 7abf7a19af..b434446b68 100644 --- a/Core/MIPS/ARM/CompVFPU.cpp +++ b/Core/MIPS/ARM/ArmCompVFPU.cpp @@ -1,8 +1,8 @@ #include "../../MemMap.h" #include "../MIPSAnalyst.h" -#include "Jit.h" -#include "RegCache.h" +#include "ArmJit.h" +#include "ArmRegCache.h" #define _RS ((op>>21) & 0x1F) diff --git a/Core/MIPS/ARM/Jit.cpp b/Core/MIPS/ARM/ArmJit.cpp similarity index 86% rename from Core/MIPS/ARM/Jit.cpp rename to Core/MIPS/ARM/ArmJit.cpp index d3c33f61c7..9dc526d59d 100644 --- a/Core/MIPS/ARM/Jit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -22,11 +22,8 @@ #include "../MIPSInt.h" #include "../MIPSTables.h" -#include "RegCache.h" -#include "Jit.h" - - -extern u32 *pspmainram; +#include "ArmRegCache.h" +#include "ArmJit.h" namespace MIPSComp { @@ -74,7 +71,7 @@ Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips) void Jit::FlushAll() { - gpr.Flush(); + gpr.FlushAll(); //fpr.Flush(FLUSH_ALL); } @@ -100,7 +97,7 @@ void Jit::Compile(u32 em_address) } int block_num = blocks.AllocateBlock(em_address); - JitBlock *b = blocks.GetBlock(block_num); + ArmJitBlock *b = blocks.GetBlock(block_num); blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, b)); } @@ -110,7 +107,7 @@ void Jit::RunLoopUntil(u64 globalticks) ((void (*)())asm_.enterCode)(); } -const u8 *Jit::DoJit(u32 em_address, JitBlock *b) +const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { js.cancel = false; js.blockStart = js.compilerPC = mips_->pc; @@ -166,42 +163,35 @@ void Jit::Comp_Generic(u32 op) void Jit::DoDownCount() { - ARMReg A = gpr.GetReg(); - ARMReg B = gpr.GetReg(); - ARMABI_MOVI2R(A, Mem(&CoreTiming::downcount)); - LDR(B, A); + ARMABI_MOVI2R(R0, Mem(&CoreTiming::downcount)); + LDR(R1, R0); if(js.downcountAmount < 255) // We can enlarge this if we used rotations { - SUBS(B, B, js.downcountAmount); - STR(A, B); + SUBS(R1, R1, js.downcountAmount); + STR(R0, R1); + } else { + // Should be fine to use R2 here, flushed the regcache anyway. + // If js.downcountAmount can be expressed as an Imm8, we don't need this anyway. + ARMABI_MOVI2R(R2, js.downcountAmount); + SUBS(R1, R1, R2); + STR(R0, R1); } - else - { - ARMReg C = gpr.GetReg(false); - ARMABI_MOVI2R(C, js.downcountAmount); - SUBS(B, B, C); - STR(A, B); - } - gpr.Unlock(A, B); } void Jit::WriteExitDestInR(ARMReg Reg) { - ARMReg A = gpr.GetReg(); - ARMABI_MOVI2R(A, (u32)&mips_->pc); - STR(A, Reg); - gpr.Unlock(Reg); // This was locked in the instruction beforehand. + ARMABI_MOVI2R(R0, (u32)&mips_->pc); + STR(R0, Reg); DoDownCount(); - ARMABI_MOVI2R(A, (u32)asm_.dispatcher); - B(A); - gpr.Unlock(A); + ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); + B(R0); } void Jit::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; + ArmJitBlock *b = js.curBlock; b->exitAddress[exit_num] = destination; b->exitPtrs[exit_num] = GetWritableCodePtr(); @@ -218,9 +208,8 @@ void Jit::WriteExit(u32 destination, int exit_num) ARMABI_MOVI2R(R0, (u32)&mips_->pc); // Watch out! This uses R14 and R12! ARMABI_MOVI2R(R1, destination); // Watch out! This uses R14 and R12! STR(R0, R1); // Watch out! This uses R14 and R12! - ARMReg A = gpr.GetReg(false); - ARMABI_MOVI2R(A, (u32)asm_.dispatcher); - B(A); + ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); + B(R0); } } diff --git a/Core/MIPS/ARM/Jit.h b/Core/MIPS/ARM/ArmJit.h similarity index 89% rename from Core/MIPS/ARM/Jit.h rename to Core/MIPS/ARM/ArmJit.h index 60e91a43cf..8d9f1de9ee 100644 --- a/Core/MIPS/ARM/Jit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -18,23 +18,18 @@ #pragma once #include "../../../Globals.h" -#include "Asm.h" - -#if !defined(ARM) -#error DO NOT BUILD ARM JIT ON NON-ARM -#endif - +#include "ArmAsm.h" #include -#include "JitCache.h" -#include "RegCache.h" +#include "ArmJitCache.h" +#include "ArmRegCache.h" namespace MIPSComp { -struct JitOptions +struct ArmJitOptions { - JitOptions() + ArmJitOptions() { enableBlocklink = false; } @@ -42,7 +37,7 @@ struct JitOptions bool enableBlocklink; }; -struct JitState +struct ArmJitState { u32 compilerPC; u32 blockStart; @@ -50,7 +45,7 @@ struct JitState bool inDelaySlot; int downcountAmount; bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block - JitBlock *curBlock; + ArmJitBlock *curBlock; }; class Jit : public ArmGen::ARMXCodeBlock @@ -66,7 +61,7 @@ public: void RunLoopUntil(u64 globalticks); void Compile(u32 em_address); // Compiles a block at current MIPS PC - const u8 *DoJit(u32 em_address, JitBlock *b); + const u8 *DoJit(u32 em_address, ArmJitBlock *b); void CompileAt(u32 addr); void Comp_RunBlock(u32 op); @@ -92,7 +87,7 @@ public: void Comp_FPU2op(u32 op); void Comp_mxc1(u32 op); - JitBlockCache *GetBlockCache() { return &blocks; } + ArmJitBlockCache *GetBlockCache() { return &blocks; } AsmRoutineManager &Asm() { return asm_; } void ClearCache(); @@ -121,9 +116,9 @@ private: void CompFPTriArith(u32 op, void (XEmitter::*arith)(X64Reg reg, OpArg), bool orderMatters); */ - JitBlockCache blocks; - JitOptions jo; - JitState js; + ArmJitBlockCache blocks; + ArmJitOptions jo; + ArmJitState js; ArmRegCache gpr; // FPURegCache fpr; diff --git a/Core/MIPS/ARM/JitCache.cpp b/Core/MIPS/ARM/ArmJitCache.cpp similarity index 84% rename from Core/MIPS/ARM/JitCache.cpp rename to Core/MIPS/ARM/ArmJitCache.cpp index 68a5aa5d3c..7c7b2c76f5 100644 --- a/Core/MIPS/ARM/JitCache.cpp +++ b/Core/MIPS/ARM/ArmJitCache.cpp @@ -29,12 +29,11 @@ #include "../MIPSTables.h" #include "../MIPSAnalyst.h" -#include "JitCache.h" -#include "../JitCommon/JitCommon.h" -#include "Asm.h" -// #include "JitBase.h" +#include "ArmEmitter.h" -#include +#include "ArmJitCache.h" +#include "../JitCommon/JitCommon.h" +#include "ArmAsm.h" #if defined USE_OPROFILE && USE_OPROFILE #include @@ -52,30 +51,30 @@ using namespace ArmGen; #define INVALID_EXIT 0xFFFFFFFF -bool JitBlock::ContainsAddress(u32 em_address) +bool ArmJitBlock::ContainsAddress(u32 em_address) { // WARNING - THIS DOES NOT WORK WITH INLINING ENABLED. return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize); } -bool JitBlockCache::IsFull() const +bool ArmJitBlockCache::IsFull() const { return GetNumBlocks() >= MAX_NUM_BLOCKS - 1; } -void JitBlockCache::Init() +void ArmJitBlockCache::Init() { MAX_NUM_BLOCKS = 65536*2; #if defined USE_OPROFILE && USE_OPROFILE agent = op_open_agent(); #endif - blocks = new JitBlock[MAX_NUM_BLOCKS]; + blocks = new ArmJitBlock[MAX_NUM_BLOCKS]; blockCodePointers = new const u8*[MAX_NUM_BLOCKS]; Clear(); } -void JitBlockCache::Shutdown() +void ArmJitBlockCache::Shutdown() { delete[] blocks; delete[] blockCodePointers; @@ -91,7 +90,7 @@ void JitBlockCache::Shutdown() #endif } -JitBlockCache::~JitBlockCache() +ArmJitBlockCache::~ArmJitBlockCache() { Shutdown(); } @@ -99,7 +98,7 @@ JitBlockCache::~JitBlockCache() // This clears the JIT cache. It's called from JitCache.cpp when the JIT cache // is full and when saving and loading states. -void JitBlockCache::Clear() +void ArmJitBlockCache::Clear() { for (int i = 0; i < num_blocks; i++) { @@ -111,7 +110,7 @@ void JitBlockCache::Clear() memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS); } -void JitBlockCache::ClearSafe() +void ArmJitBlockCache::ClearSafe() { #ifdef JIT_UNLIMITED_ICACHE memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE); @@ -129,23 +128,23 @@ void JitBlockCache::ClearSafe() } }*/ -void JitBlockCache::Reset() +void ArmJitBlockCache::Reset() { Shutdown(); Init(); } -JitBlock *JitBlockCache::GetBlock(int no) +ArmJitBlock *ArmJitBlockCache::GetBlock(int no) { return &blocks[no]; } -int JitBlockCache::GetNumBlocks() const +int ArmJitBlockCache::GetNumBlocks() const { return num_blocks; } -bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const +bool ArmJitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const { // check if any endpoint is inside the other range if ((s1 >= s2 && s1 <= e2) || @@ -157,9 +156,9 @@ bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const return false; } -int JitBlockCache::AllocateBlock(u32 em_address) +int ArmJitBlockCache::AllocateBlock(u32 em_address) { - JitBlock &b = blocks[num_blocks]; + ArmJitBlock &b = blocks[num_blocks]; b.invalid = false; b.originalAddress = em_address; b.exitAddress[0] = INVALID_EXIT; @@ -173,10 +172,10 @@ int JitBlockCache::AllocateBlock(u32 em_address) return num_blocks - 1; } -void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr) +void ArmJitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr) { blockCodePointers[block_num] = code_ptr; - JitBlock &b = blocks[block_num]; + ArmJitBlock &b = blocks[block_num]; b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress); u32 opcode = MIPS_MAKE_EMUHACK(0, block_num); @@ -222,12 +221,12 @@ void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code #endif } -const u8 **JitBlockCache::GetCodePointers() +const u8 **ArmJitBlockCache::GetCodePointers() { return blockCodePointers; } -int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr) +int ArmJitBlockCache::GetBlockNumberFromStartAddress(u32 addr) { if (!blocks) return -1; @@ -242,14 +241,14 @@ int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr) return bl; } -void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector *block_numbers) +void ArmJitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector *block_numbers) { for (int i = 0; i < num_blocks; i++) if (blocks[i].ContainsAddress(em_address)) block_numbers->push_back(i); } -u32 JitBlockCache::GetOriginalFirstOp(int block_num) +u32 ArmJitBlockCache::GetOriginalFirstOp(int block_num) { if (block_num >= num_blocks) { @@ -259,12 +258,12 @@ u32 JitBlockCache::GetOriginalFirstOp(int block_num) return blocks[block_num].originalFirstOpcode; } -CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num) +CompiledCode ArmJitBlockCache::GetCompiledCodeFromBlock(int block_num) { return (CompiledCode)blockCodePointers[block_num]; } -std::string JitBlockCache::GetCompiledDisassembly(int block_num) +std::string ArmJitBlockCache::GetCompiledDisassembly(int block_num) { /* std::string buf; @@ -287,9 +286,9 @@ std::string JitBlockCache::GetCompiledDisassembly(int block_num) //Can be faster by doing a queue for blocks to link up, and only process those //Should probably be done -void JitBlockCache::LinkBlockExits(int i) +void ArmJitBlockCache::LinkBlockExits(int i) { - JitBlock &b = blocks[i]; + ArmJitBlock &b = blocks[i]; if (b.invalid) { // This block is dead. Don't relink it. @@ -312,10 +311,10 @@ void JitBlockCache::LinkBlockExits(int i) using namespace std; -void JitBlockCache::LinkBlock(int i) +void ArmJitBlockCache::LinkBlock(int i) { LinkBlockExits(i); - JitBlock &b = blocks[i]; + ArmJitBlock &b = blocks[i]; std::map::iterator iter; pair::iterator, multimap::iterator> ppp; // equal_range(b) returns pair representing the range @@ -329,16 +328,16 @@ void JitBlockCache::LinkBlock(int i) } } -void JitBlockCache::UnlinkBlock(int i) +void ArmJitBlockCache::UnlinkBlock(int i) { - JitBlock &b = blocks[i]; + ArmJitBlock &b = blocks[i]; std::map::iterator iter; pair::iterator, multimap::iterator> ppp; ppp = links_to.equal_range(b.originalAddress); if (ppp.first == ppp.second) return; for (multimap::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) { - JitBlock &sourceBlock = blocks[iter2->second]; + ArmJitBlock &sourceBlock = blocks[iter2->second]; for (int e = 0; e < 2; e++) { if (sourceBlock.exitAddress[e] == b.originalAddress) @@ -347,14 +346,14 @@ void JitBlockCache::UnlinkBlock(int i) } } -void JitBlockCache::DestroyBlock(int block_num, bool invalidate) +void ArmJitBlockCache::DestroyBlock(int block_num, bool invalidate) { if (block_num < 0 || block_num >= num_blocks) { ERROR_LOG(JIT, "DestroyBlock: Invalid block number %d", block_num); return; } - JitBlock &b = blocks[block_num]; + ArmJitBlock &b = blocks[block_num]; if (b.invalid) { if (invalidate) @@ -382,7 +381,7 @@ void JitBlockCache::DestroyBlock(int block_num, bool invalidate) */ } -void JitBlockCache::InvalidateICache(u32 address, const u32 length) +void ArmJitBlockCache::InvalidateICache(u32 address, const u32 length) { // Convert the logical address to a physical address for the block map u32 pAddr = address & 0x1FFFFFFF; diff --git a/Core/MIPS/ARM/JitCache.h b/Core/MIPS/ARM/ArmJitCache.h similarity index 95% rename from Core/MIPS/ARM/JitCache.h rename to Core/MIPS/ARM/ArmJitCache.h index ae38f06e6a..d464417115 100644 --- a/Core/MIPS/ARM/JitCache.h +++ b/Core/MIPS/ARM/ArmJitCache.h @@ -22,7 +22,7 @@ #include #include "../MIPSAnalyst.h" - +#include "../MIPS.h" // Define this in order to get VTune profile support for the Jit generated code. // Add the VTune include/lib directories to the project directories to get this to build. // #define USE_VTUNE @@ -42,7 +42,7 @@ #define JIT_OPCODE 0xFFCCCCCC // yeah this ain't gonna work -struct JitBlock +struct ArmJitBlock { const u8 *checkedEntry; const u8 *normalEntry; @@ -77,11 +77,11 @@ struct JitBlock typedef void (*CompiledCode)(); -class JitBlockCache +class ArmJitBlockCache { MIPSState *mips; const u8 **blockCodePointers; - JitBlock *blocks; + ArmJitBlock *blocks; int num_blocks; std::multimap links_to; std::map, u32> block_map; // (end_addr, start_addr) -> number @@ -94,10 +94,10 @@ class JitBlockCache void UnlinkBlock(int i); public: - JitBlockCache(MIPSState *mips_) : + ArmJitBlockCache(MIPSState *mips_) : mips(mips_), blockCodePointers(0), blocks(0), num_blocks(0), MAX_NUM_BLOCKS(0) { } - ~JitBlockCache(); + ~ArmJitBlockCache(); int AllocateBlock(u32 em_address); void FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr); @@ -110,7 +110,7 @@ public: bool IsFull() const; // Code Cache - JitBlock *GetBlock(int block_num); + ArmJitBlock *GetBlock(int block_num); int GetNumBlocks() const; const u8 **GetCodePointers(); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp new file mode 100644 index 0000000000..83037335df --- /dev/null +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -0,0 +1,189 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "ArmABI.h" +#include "ArmRegCache.h" +#include "ArmEmitter.h" + +using namespace ArmGen; + +#define CTXREG ((ARMReg)14) + +ArmRegCache::ArmRegCache(MIPSState *mips) : mips_(mips) { +} + +void ArmRegCache::Init(ARMXEmitter *emitter) { + emit = emitter; +} + +void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { + for (int i = 0; i < 16; i++) { + ar[i].mipsReg = -1; + ar[i].spillLock = false; + ar[i].allocLock = false; + ar[i].isDirty = false; + } + for (int i = 0; i < 32; i++) { + mr[i].loc = ML_MEM; + } +} + +static const ARMReg *GetMIPSAllocationOrder(int &count) { + // Note that R0 and R1 are reserved as scratch for now. We can probably free up R1 eventually. + static const ARMReg allocationOrder[] = { + R2, R3, R4, R5, R6, R7, R8, R9 + }; + count = sizeof(allocationOrder) / sizeof(const int); + return allocationOrder; +} + +ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { + // Let's see if it's already mapped. + for (int i = 0; i < NUM_ARMREG; i++) { + if (ar[i].mipsReg == mipsReg) { + // Already mapped, no need to do anything more. + return (ARMReg)i; + } + } + + // Okay, so we need to allocate one. + + int allocCount; + const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount); + +allocate: + for (int i = 0; i < allocCount; i++) { + int reg = allocOrder[i]; + + if (ar[reg].mipsReg == -1 && !ar[reg].allocLock) { + // That means it's free. Grab it, and load the value into it (if requested). + ar[reg].mipsReg = mipsReg; + ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; + if (mapFlags & MAP_INITVAL) { + if (mr[mipsReg].loc == ML_MEM) + emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); + else if (mr[mipsReg].loc == ML_IMM) + emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm); + } + return (ARMReg)reg; + } + } + + // Still nothing. Let's spill a reg and goto 10 + + for (int i = 0; i < allocCount; i++) { + if (ar[i].spillLock || ar[i].allocLock) + continue; + FlushArmReg((ARMReg)i); + goto allocate; + } + + // Uh oh, we have all them alloclocked and spilllocked.... + _assert_msg_(JIT, false, "All available registers are locked dumb dumb"); + return INVALID_REG; +} + +void ArmRegCache::FlushArmReg(ARMReg r) { + if (ar[r].mipsReg == -1) { + // Nothing to do + return; + } + if (ar[r].isDirty) { + if (mr[ar[r].mipsReg].loc == ML_MEM) + emit->STR(r, CTXREG, 4 * ar[r].mipsReg); + } +} + +void ArmRegCache::FlushMipsReg(MIPSReg r) { + switch (mr[r].loc) { + case ML_IMM: + // IMM is always "dirty". + emit->ARMABI_MOVI2R(R0, mr[r].imm); + emit->STR(R0, CTXREG, GetMipsRegOffset(r)); + break; + + case ML_ARMREG: + if (ar[mr[r].reg].isDirty) + emit->STR(mr[r].reg, CTXREG, GetMipsRegOffset(r)); + ar[mr[r].reg].mipsReg = -1; + ar[mr[r].reg].isDirty = false; + break; + + default: + //BAD + break; + } + mr[r].loc = ML_MEM; +} + +void ArmRegCache::FlushAll() { + for (int i = 0; i < NUM_MIPSREG; i++) { + FlushMipsReg(i); + } +} + +void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { + // Zap existing value + if (mr[r].loc == ML_ARMREG) + ar[mr[r].reg].mipsReg = -1; + mr[r].loc = ML_IMM; + mr[r].imm = immVal; +} + +bool ArmRegCache::IsImm(MIPSReg r) const { + return mr[r].loc == ML_IMM; +} + +u32 ArmRegCache::GetImm(MIPSReg r) const { + // TODO: Check. + return mr[r].imm; +} + +int ArmRegCache::GetMipsRegOffset(MIPSReg r) { + if (r < 32) + return r * 4; + switch (r) { + case MIPSREG_HI: + return offsetof(MIPSState, hi); + case MIPSREG_LO: + return offsetof(MIPSState, lo); + } + _dbg_assert_msg_(JIT, false, "bad mips register %i", (int)r); + return -999; // boom! +} + +void ArmRegCache::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3) { + if (mr[r1].loc == ML_ARMREG) ar[mr[r1].reg].spillLock = true; + if (r2 != -1 && mr[r2].loc == ML_ARMREG) ar[mr[r2].reg].spillLock = true; + if (r3 != -1 && mr[r3].loc == ML_ARMREG) ar[mr[r3].reg].spillLock = true; +} + +void ArmRegCache::ReleaseSpillLocks() { + for (int i = 0; i < 16; i++) { + ar[i].spillLock = false; + } +} + +ARMReg ArmRegCache::R(int mipsReg) { + if (mr[mipsReg].loc == ML_ARMREG) { + return mr[mipsReg].reg; + } else { + _dbg_assert_msg_(JIT, false, "R: not mapped"); + return INVALID_REG; // BAAAD + } +} + diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h new file mode 100644 index 0000000000..dfbcf2b4da --- /dev/null +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -0,0 +1,112 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#pragma once + +#include "ArmEmitter.h" +#include "../MIPS.h" +#include "../MIPSAnalyst.h" +#include "ArmABI.h" + +using namespace ArmGen; + +// R2 to R9: mapped MIPS regs +// R11 = base pointer +// R12 = MIPS context +// R14 = code pointers + +// Special MIPS registers: +enum { + MIPSREG_HI = 32, + MIPSREG_LO = 33, + TOTAL_MAPPABLE_MIPSREGS = 34, +}; + +typedef int MIPSReg; + +struct RegARM { + int mipsReg; // if -1, no mipsreg attached. + bool isDirty; // Should the register be written back? + bool allocLock; // if true, this ARM register cannot be used for allocation. + bool spillLock; // if true, this register cannot be spilled. +}; + +enum RegMIPSLoc { + ML_IMM, + ML_ARMREG, + ML_MEM, +}; + +struct RegMIPS { + // Where is this MIPS register? + RegMIPSLoc loc; + // Data (only one of these is used, depending on loc. Could make a union). + u32 imm; + ARMReg reg; + // If loc == ML_MEM, it's back in its location in the CPU context struct. +}; + +enum { + MAP_DIRTY = 1, + MAP_INITVAL = 2, +}; + +class ArmRegCache +{ +public: + ArmRegCache(MIPSState *mips); + ~ArmRegCache() {} + + void Init(ARMXEmitter *emitter); + void Start(MIPSAnalyst::AnalysisResults &stats); + + // void AllocLock(ARMReg reg, ARMReg reg2 = INVALID_REG, ARMReg reg3 = INVALID_REG); + // void ReleaseAllocLock(ARMReg reg); + + // Protect the arm register containing a MIPS register from spilling, to ensure that + // it's being kept allocated. + void SpillLock(MIPSReg reg, MIPSReg reg2 = -1, MIPSReg reg3 = -1); + void ReleaseSpillLocks(); + + void SetImm(MIPSReg reg, u32 immVal); + bool IsImm(MIPSReg reg) const; + u32 GetImm(MIPSReg reg) const; + + // Returns an ARM register containing the requested MIPS register. + ARMReg MapReg(MIPSReg reg, int mapFlags = 0); + void FlushArmReg(ARMReg r); + void FlushMipsReg(MIPSReg r); + + void FlushAll(); + + ARMReg R(int preg); // Returns a cached register + + void SetEmitter(ARMXEmitter *emitter) { emit = emitter; } + +private: + int GetMipsRegOffset(MIPSReg r); + MIPSState *mips_; + ARMXEmitter *emit; + + enum { + NUM_ARMREG = 16, + NUM_MIPSREG = TOTAL_MAPPABLE_MIPSREGS, + }; + + RegARM ar[16]; + RegMIPS mr[32]; +}; diff --git a/Core/MIPS/ARM/RegCache.cpp b/Core/MIPS/ARM/RegCache.cpp deleted file mode 100644 index 80ec9ec8f3..0000000000 --- a/Core/MIPS/ARM/RegCache.cpp +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "RegCache.h" -#include "ArmEmitter.h" - -using namespace ArmGen; - -/* -// these are MIPS reg indices -void RegCache::Lock(int p1, int p2, int p3, int p4) -{ - locks[p1] = true; - if (p2 != 0xFF) locks[p2] = true; - if (p3 != 0xFF) locks[p3] = true; - if (p4 != 0xFF) locks[p4] = true; -} - -// these are x64 reg indices -void RegCache::LockX(int x1, int x2, int x3, int x4) -{ - if (xlocks[x1]) { - PanicAlert("RegCache: x %i already locked!", x1); - } - xlocks[x1] = true; - if (x2 != 0xFF) xlocks[x2] = true; - if (x3 != 0xFF) xlocks[x3] = true; - if (x4 != 0xFF) xlocks[x4] = true; -} - -bool RegCache::IsFreeX(int xreg) const -{ - return xregs[xreg].free && !xlocks[xreg]; -} - -void RegCache::UnlockAll() -{ - for (int i = 0; i < 32; i++) - locks[i] = false; -} -*/ - -ArmRegCache::ArmRegCache(MIPSState *mips) - : mips_(mips) -{ - emit = 0; - -} - -void ArmRegCache::Init(ARMXEmitter *emitter) -{ - emit = emitter; - ARMReg *PPCRegs = GetMIPSAllocationOrder(NUMMIPSREG); - ARMReg *Regs = GetAllocationOrder(NUMARMREG); - for(int a = 0; a < 32; ++a) - { - // This gives us the memory locations of the gpr registers so we can - // load them. - regs[a].location = (u8*)&mips_->r[a]; - } - for(int a = 0; a < NUMMIPSREG; ++a) - { - ArmCRegs[a].MIPSReg = 33; - ArmCRegs[a].Reg = PPCRegs[a]; - ArmCRegs[a].LastLoad = 0; - } - for(int a = 0; a < NUMARMREG; ++a) - { - ArmRegs[a].Reg = Regs[a]; - ArmRegs[a].free = true; - } -} - -void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) -{ - for(int a = 0; a < NUMMIPSREG; ++a) - { - ArmCRegs[a].MIPSReg = 33; - ArmCRegs[a].LastLoad = 0; - } -} - -ARMReg *ArmRegCache::GetMIPSAllocationOrder(int &count) -{ - // This will return us the allocation order of the registers we can use on - // the MIPS side. - static ARMReg allocationOrder[] = - { - R0, R1, R2, R3, R4, R5, R6, R7, R8, R9 - }; - count = sizeof(allocationOrder) / sizeof(const int); - return allocationOrder; -} -ARMReg *ArmRegCache::GetAllocationOrder(int &count) -{ - // This will return us the allocation order of the registers we can use on - // the host side. - static ARMReg allocationOrder[] = - { - R14, R12, R11, R10 - }; - count = sizeof(allocationOrder) / sizeof(const int); - return allocationOrder; -} - -ARMReg ArmRegCache::GetReg(bool AutoLock) -{ - for (int a = 0; a < NUMARMREG; ++a) - { - if(ArmRegs[a].free) - { - // Alright, this one is free - if (AutoLock) - ArmRegs[a].free = false; - return ArmRegs[a].Reg; - } - } - // Uh Oh, we have all them locked.... - _assert_msg_(JIT, false, "All available registers are locked dumb dumb"); -} -void ArmRegCache::Lock(ARMReg Reg) -{ - for (int RegNum = 0; RegNum < NUMARMREG; ++RegNum) - { - if(ArmRegs[RegNum].Reg == Reg) - { - _assert_msg_(JIT, ArmRegs[RegNum].free, "This register is already locked"); - ArmRegs[RegNum].free = false; - } - } - _assert_msg_(JIT, false, "Register %d can't be used with lock", Reg); -} -void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3) -{ - for (int RegNum = 0; RegNum < NUMARMREG; ++RegNum) - { - if(ArmRegs[RegNum].Reg == R0) - { - _assert_msg_(JIT, !ArmRegs[RegNum].free, "This register is already unlocked"); - ArmRegs[RegNum].free = true; - } - if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true; - if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true; - if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true; - } -} - -ARMReg ArmRegCache::R(int preg) -{ - u32 HighestUsed = 0; - u8 Num = 0; - for (int a = 0; a < NUMMIPSREG; ++a){ - ++ArmCRegs[a].LastLoad; - if (ArmCRegs[a].LastLoad > HighestUsed) - { - HighestUsed = ArmCRegs[a].LastLoad; - Num = a; - } - } - // Check if already Loaded - for (int a = 0; a < NUMMIPSREG; ++a) { - if (ArmCRegs[a].MIPSReg == preg) - { - ArmCRegs[a].LastLoad = 0; - return ArmCRegs[a].Reg; - } - } - // Check if we have a free register - for (u8 a = 0; a < NUMMIPSREG; ++a) - if (ArmCRegs[a].MIPSReg == 33) - { - emit->ARMABI_MOVI2R(ArmCRegs[a].Reg, (u32)&mips_->r); - emit->LDR(ArmCRegs[a].Reg, ArmCRegs[a].Reg, preg * 4); - ArmCRegs[a].MIPSReg = preg; - ArmCRegs[a].LastLoad = 0; - return ArmCRegs[a].Reg; - } - // Alright, we couldn't get a free space, dump that least used register - // Note that this is incredibly dangerous if references to the register - // are still floating around out there! - ARMReg rA = GetReg(false); - emit->ARMABI_MOVI2R(rA, (u32)&mips_->r); - emit->STR(rA, ArmCRegs[Num].Reg, ArmCRegs[Num].MIPSReg * 4); - emit->LDR(ArmCRegs[Num].Reg, rA, preg * 4); - ArmCRegs[Num].MIPSReg = preg; - ArmCRegs[Num].LastLoad = 0; - return ArmCRegs[Num].Reg; -} - -void ArmRegCache::Flush() -{ - // Maybe we should keep this pointer around permanently? - emit->MOVW(R14, (u32)&mips_->r); - emit->MOVT(R14, (u32)&mips_->r, true); - - for (int a = 0; a < NUMMIPSREG; ++a) { - if (ArmCRegs[a].MIPSReg != 33) - { - emit->STR(R14, ArmCRegs[a].Reg, ArmCRegs[a].MIPSReg * 4); - ArmCRegs[a].MIPSReg = 33; - ArmCRegs[a].LastLoad = 0; - } - } -} - diff --git a/Core/MIPS/ARM/RegCache.h b/Core/MIPS/ARM/RegCache.h deleted file mode 100644 index ab5d85c0ca..0000000000 --- a/Core/MIPS/ARM/RegCache.h +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright (C) 2003 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifndef _JITARMREGCACHE_H -#define _JITARMREGCACHE_H - -#include "ArmEmitter.h" -#include "../MIPS.h" -#include "../MIPSAnalyst.h" -#include "ArmABI.h" - -using namespace ArmGen; - -// This ARM Register cache actually pre loads the most used registers before -// the block to increase speed since every memory load requires two -// instructions to load it. We are going to use R0-RMAX as registers for the -// use of MIPS Registers. -// Allocation order as follows -#define ARMREGS 16 -// Allocate R0 to R9 for MIPS first. -// For General registers on the host side, start with R14 and go down as we go -// R13 is reserved for our stack pointer, don't ever use that. Unless you save -// it -// So we have R14, R12, R11, R10 to work with instructions - -struct MIPSCachedReg -{ - const u8 *location; -}; - -struct JRCPPC -{ - u32 MIPSReg; // Tied to which MIPS Register - ARMReg Reg; // Tied to which ARM Register - u32 LastLoad; -}; -struct JRCReg -{ - ARMReg Reg; // Which reg this is. - bool free; -}; -class ArmRegCache -{ -private: - MIPSCachedReg regs[32]; - JRCPPC ArmCRegs[ARMREGS]; - JRCReg ArmRegs[ARMREGS]; // Four registers remaining - - int NUMMIPSREG; // + LO, HI, ... - int NUMARMREG; - - ARMReg *GetAllocationOrder(int &count); - ARMReg *GetMIPSAllocationOrder(int &count); - - MIPSState *mips_; - -protected: - ARMXEmitter *emit; - -public: - ArmRegCache(MIPSState *mips); - ~ArmRegCache() {} - - void Init(ARMXEmitter *emitter); - void Start(MIPSAnalyst::AnalysisResults &stats); - - void SetEmitter(ARMXEmitter *emitter) {emit = emitter;} - - // TODO: Add a way to lock MIPS registers so they aren't kicked out when you don't expect it. - - ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use. - void Lock(ARMReg R0); - void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 = INVALID_REG); - void Flush(); - ARMReg R(int preg); // Returns a cached register - -}; - - - - -#endif diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index a7b4673484..a8630e6fbe 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -18,7 +18,7 @@ #pragma once #if defined(ARM) -#include "../ARM/Jit.h" +#include "../ARM/ArmJit.h" #else #include "../x86/Jit.h" #endif diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index d784419212..b7d89cd873 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -24,8 +24,8 @@ #include "../HLE/sceDisplay.h" #if defined(ARM) -#include "ARM/JitCache.h" -#include "ARM/Jit.h" +#include "ARM/ArmJitCache.h" +#include "ARM/ArmJit.h" #else #include "x86/JitCache.h" #include "x86/Jit.h" @@ -40,7 +40,6 @@ MIPSDebugInterface *currentDebugMIPS = &debugr4k; MIPSState::MIPSState() { - cpuType = CPUTYPE_ALLEGREX; MIPSComp::jit = 0; } @@ -57,7 +56,7 @@ void MIPSState::Reset() delete MIPSComp::jit; MIPSComp::jit = 0; } - + if (PSP_CoreParameter().cpuCore == CPU_JIT) MIPSComp::jit = new MIPSComp::Jit(this); @@ -90,7 +89,6 @@ void MIPSState::Reset() fcr0 = 0; fcr31 = 0; debugCount = 0; - exceptions = 0; currentMIPS = this; inDelaySlot = false; llBit = 0; @@ -120,8 +118,6 @@ void MIPSState::DoState(PointerWrap &p) rng.DoState(p); p.Do(inDelaySlot); p.Do(llBit); - p.Do(cpuType); - p.Do(exceptions); p.Do(debugCount); p.DoMarker("MIPSState"); } diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 1e8bd29a6f..92c440e606 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -124,22 +124,15 @@ public: u32 hi; u32 lo; - u32 fpcond; //separate for speed - TODO: not worth it - u32 fcr0; u32 fcr31; //fpu control register + u32 fpcond; // cache the cond flag of fcr31 (& 1 << 23) GMRng rng; // VFPU hardware random number generator. Probably not the right type. bool inDelaySlot; int llBit; // ll/sc - CPUType cpuType; - - // TODO: How do we handle exceptions? - u32 exceptions; - enum { BREAKING_EXCEPTIONS = 1 }; - // Debug stuff u32 debugCount; // can be used to count basic blocks before crashes, etc. diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 08cfa7d0c0..715d8bef10 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -149,43 +149,4 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //Landing pad for drec space ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); - - GenerateCommon(); -} - -void AsmRoutineManager::GenerateCommon() -{ - /* - fifoDirectWrite8 = AlignCode4(); - GenFifoWrite(8); - fifoDirectWrite16 = AlignCode4(); - GenFifoWrite(16); - fifoDirectWrite32 = AlignCode4(); - GenFifoWrite(32); - fifoDirectWriteFloat = AlignCode4(); - GenFifoFloatWrite(); - fifoDirectWriteXmm64 = AlignCode4(); - GenFifoXmm64Write(); - - GenQuantizedLoads(); - GenQuantizedStores(); - GenQuantizedSingleStores(); - */ - - //CMPSD(R(XMM0), M(&zero), - // TODO - - // Fast write routines - special case the most common hardware write - // TODO: use this. - // Even in x86, the param values will be in the right registers. - /* - const u8 *fastMemWrite8 = AlignCode16(); - CMP(32, R(ABI_PARAM2), Imm32(0xCC008000)); - FixupBranch skip_fast_write = J_CC(CC_NE, false); - MOV(32, EAX, M(&m_gatherPipeCount)); - MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1); - ADD(32, 1, M(&m_gatherPipeCount)); - RET(); - SetJumpTarget(skip_fast_write); - CALL((void *)&Memory::Write_U8);*/ -} +} \ No newline at end of file diff --git a/Core/MIPS/x86/Asm.h b/Core/MIPS/x86/Asm.h index 226332c676..d60aa4172d 100644 --- a/Core/MIPS/x86/Asm.h +++ b/Core/MIPS/x86/Asm.h @@ -20,19 +20,8 @@ #include "x64Emitter.h" #include "../MIPS.h" -// In Dolphin, we don't use inline assembly. Instead, we generate all machine-near -// code at runtime. In the case of fixed code like this, after writing it, we write -// protect the memory, essentially making it work just like precompiled code. -// There are some advantages to this approach: -// 1) No need to setup an external assembler in the build. -// 2) Cross platform, as long as it's x86/x64. -// 3) Can optimize code at runtime for the specific CPU model. -// There aren't really any disadvantages other than having to maintain a x86 emitter, -// which we have to do anyway :) -// -// To add a new asm routine, just add another const here, and add the code to Generate. -// Also, possibly increase the size of the code buffer. +// Runtime generated assembly routines, like the Dispatcher. namespace MIPSComp { @@ -68,8 +57,6 @@ public: const u8 *dispatcherCheckCoreState; const u8 *dispatcherNoCheck; - const u8 *fpException; - const u8 *breakpointBailout; }; diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 58b04562fc..cfdc23963d 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -25,9 +25,6 @@ #include "RegCache.h" #include "Jit.h" - -extern u32 *pspmainram; - namespace MIPSComp { @@ -52,8 +49,6 @@ void Jit::ClearCache() ClearCodeSpace(); } -u8 *codeCache; -#define CACHESIZE 16384*1024 void Jit::CompileAt(u32 addr) { u32 op = Memory::Read_Instruction(addr); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 0b99805fe0..4e54d3bcff 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -168,14 +168,15 @@ LOCAL_SRC_FILES := \ $(SRC)/Core/MIPS/MIPSCodeUtils.cpp \ $(SRC)/Core/MIPS/MIPSDebugInterface.cpp \ $(SRC)/Core/MIPS/JitCommon/JitCommon.cpp \ - $(SRC)/Core/MIPS/ARM/JitCache.cpp \ - $(SRC)/Core/MIPS/ARM/CompALU.cpp \ - $(SRC)/Core/MIPS/ARM/CompBranch.cpp \ - $(SRC)/Core/MIPS/ARM/CompFPU.cpp \ - $(SRC)/Core/MIPS/ARM/Asm.cpp \ - $(SRC)/Core/MIPS/ARM/Jit.cpp \ - $(SRC)/Core/MIPS/ARM/CompLoadStore.cpp \ - $(SRC)/Core/MIPS/ARM/RegCache.cpp \ + $(SRC)/Core/MIPS/ARM/ArmJitCache.cpp \ + $(SRC)/Core/MIPS/ARM/ArmCompALU.cpp \ + $(SRC)/Core/MIPS/ARM/ArmCompBranch.cpp \ + $(SRC)/Core/MIPS/ARM/ArmCompFPU.cpp \ + $(SRC)/Core/MIPS/ARM/ArmCompLoadStore.cpp \ + $(SRC)/Core/MIPS/ARM/ArmCompVFPU.cpp \ + $(SRC)/Core/MIPS/ARM/ArmAsm.cpp \ + $(SRC)/Core/MIPS/ARM/ArmJit.cpp \ + $(SRC)/Core/MIPS/ARM/ArmRegCache.cpp \ $(SRC)/Core/Util/BlockAllocator.cpp \ $(SRC)/Core/Util/ppge_atlas.cpp \ $(SRC)/Core/Util/PPGeDraw.cpp diff --git a/android/jni/Application.mk b/android/jni/Application.mk index dd83bdcd41..62367cb819 100644 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,2 +1,5 @@ APP_STL := stlport_static -APP_ABI := armeabi armeabi-v7a +APP_ABI := armeabi-v7a + + +#armeabi From 5a7f4acc0645b8f02e4c9776d15d4a5495d3e7b6 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 00:26:42 +0100 Subject: [PATCH 07/49] More armjit work --- Common/ArmEmitter.h | 2 + Core/MIPS/ARM/ArmAsm.cpp | 88 ++++++++++++------- Core/MIPS/ARM/ArmAsm.h | 15 ++-- Core/MIPS/ARM/ArmCompBranch.cpp | 82 ++++++++++++----- Core/MIPS/ARM/ArmJit.cpp | 1 + Core/MIPS/ARM/ArmJit.h | 5 +- Core/MIPS/ARM/ArmRegCache.cpp | 2 +- Core/MIPS/x86/Asm.h | 8 +- android/jni/EmuScreen.cpp | 4 - android/jni/MenuScreens.cpp | 11 +-- .../src/org/ppsspp/ppsspp/PpssppActivity.java | 2 +- 11 files changed, 141 insertions(+), 79 deletions(-) diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 1599296e63..a55671ffa2 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -112,6 +112,8 @@ enum OpType TYPE_MEM }; + +// This is no longer a proper operand2 class. Need to split up. class Operand2 { friend class ARMXEmitter; diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index e93e755fae..91139a8b18 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -56,7 +56,8 @@ static bool enableDebug = false; // STATIC ALLOCATION ARM: // R11 : Memory base pointer. - +// R10 : MIPS state +// R9 : extern volatile CoreState coreState; void Jit() @@ -64,54 +65,73 @@ void Jit() MIPSComp::jit->Compile(currentMIPS->pc); } +void ImHere() { + static int i = 0; + i++; + INFO_LOG(HLE, "I'm too here %i", i); +} +void ImHere2(u32 hej) { + static int i = 0; + i++; + INFO_LOG(HLE, "I'm here2 %i %08x", i, hej); +} // PLAN: no more block numbers - crazy opcodes just contain offset within // dynarec buffer // At this offset - 4, there is an int specifying the block number. -void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) +void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) { enterCode = AlignCode16(); - PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); SetCC(CC_AL); + PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); + // Fixed registers, these are always kept when in Jit context. // R13 cannot be used as it's the stack pointer. ARMABI_MOVI2R(R11, (u32)Memory::base); - ARMABI_MOVI2R(R12, (u32)currentMIPS); - ARMABI_MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers()); + ARMABI_MOVI2R(R10, (u32)mips); + ARMABI_MOVI2R(R9, (u32)jit->GetBlockCache()->GetCodePointers()); + + // PROVEN: We Get Here + ARMABI_CallFunction((void *)&ImHere); outerLoop = GetCodePtr(); - //ARMABI_CallFunction(reinterpret_cast(&CoreTiming::Advance)); + ARMABI_CallFunction((void *)&CoreTiming::Advance); FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time dispatcherCheckCoreState = GetCodePtr(); - //TODO: critical - //CMP(32, M((void*)&coreState), Imm32(0)); - FixupBranch badCoreState; // = J_CC(CC_NZ, true); + // TODO: critical + ARMABI_MOVI2R(R0, (u32)&coreState); + LDR(R0, R0); + CMP(R0, 0); + FixupBranch badCoreState = B_CC(CC_NEQ); + + // 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!!! - SetCC(CC_LT); - FixupBranch bail = B(); - - SetCC(CC_AL); + FixupBranch bail = B_CC(CC_LT); + SetJumpTarget(skipToRealDispatch); - //INT3(); dispatcherNoCheck = GetCodePtr(); - // LDR(R0, R11, R0, M(&mips->pc)); - dispatcherPcInR0 = GetCodePtr(); - _assert_msg_(CPU, Memory::base != 0, "Memory base bogus"); - //ARMABI_MOVIMM32(R1, Memory::MEMVIEW32_MASK + 1); + // Debug + + ARMABI_MOVI2R(R0, (u32)&mips->pc); + LDR(R0, R0); + + ARMABI_MOVI2R(R1, Memory::MEMVIEW32_MASK); // can be done with single MOVN instruction AND(R0, R0, R1); - LDR(R0, R11, R0); - AND(R1, R0, Operand2(0xFC, 24)); - BIC(R0, R0, Operand2(0xFC, 24)); - CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 24)); + ARMABI_CallFunction((void *)&ImHere2); + + LDR(R0, R11, R(R0)); + AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. + BIC(R0, R0, Operand2(0xFC, 4)); + CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); FixupBranch notfound = B_CC(CC_NEQ); // IDEA - we have 24 bits, why not just use offsets from base of code? if (enableDebug) @@ -119,24 +139,32 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //ADD(32, M(&mips->debugCount), Imm8(1)); } // grab from list and jump to it - ADD(R0, R10, Operand2(2, ST_LSL, R0)); + ADD(R0, R14, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); + SetJumpTarget(notfound); + ARMABI_CallFunction((void *)&ImHere); + + //Ok, no block, let's jit ARMABI_CallFunction((void *)&Jit); + B(dispatcherNoCheck); // no point in special casing this SetJumpTarget(bail); - SetJumpTarget(badCoreState); + // TODO: critical + ARMABI_MOVI2R(R0, (u32)&coreState); + LDR(R0, R0); + CMP(R0, 0); + B_CC(CC_EQ, outerLoop); - //CMP(M((void*)&coreState), Imm8(0)); - SetCC(CC_EQ); - //B(outerLoop); - SetCC(CC_AL); + SetJumpTarget(badCoreState); - //Landing pad for drec space + ARMABI_CallFunction((void *)&ImHere); - PUSH(8, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns + //Landing pad for drec space + + POP(8, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns } \ No newline at end of file diff --git a/Core/MIPS/ARM/ArmAsm.h b/Core/MIPS/ARM/ArmAsm.h index 459582452e..faba14a49d 100644 --- a/Core/MIPS/ARM/ArmAsm.h +++ b/Core/MIPS/ARM/ArmAsm.h @@ -28,16 +28,20 @@ namespace MIPSComp class Jit; } -class AsmRoutineManager : public ArmGen::ARMXCodeBlock +class ArmAsmRoutineManager : public ArmGen::ARMXCodeBlock { private: void Generate(MIPSState *mips, MIPSComp::Jit *jit); void GenerateCommon(); public: - AsmRoutineManager() + ArmAsmRoutineManager() { } + ~ArmAsmRoutineManager() + { + FreeCodeSpace(); + } void Init(MIPSState *mips, MIPSComp::Jit *jit) { @@ -45,10 +49,6 @@ public: Generate(mips, jit); WriteProtect(); } - ~AsmRoutineManager() - { - FreeCodeSpace(); - } const u8 *enterCode; @@ -56,7 +56,8 @@ public: const u8 *dispatcherCheckCoreState; const u8 *dispatcher; const u8 *dispatcherNoCheck; - const u8 *dispatcherPcInR0; + + const u8 *breakpointBailout; }; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 750589b654..799bbf94f2 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -193,7 +193,6 @@ void Jit::Comp_RelBranchRI(u32 op) js.compiling = false; } - // If likely is set, discard the branch slot if NOT taken. void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) { @@ -209,9 +208,9 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) } FlushAll(); - ARMABI_MOVI2R(R0, (u32)&(mips_->fcr31)); + ARMABI_MOVI2R(R0, (u32)&(mips_->fpcond)); LDR(R0, R0, Operand2(0, TYPE_IMM)); - TST(R0, Operand2(1 << 23, TYPE_IMM)); + TST(R0, Operand2(1, TYPE_IMM)); ArmGen::FixupBranch ptr; js.inDelaySlot = true; if (!likely) @@ -259,29 +258,68 @@ void Jit::Comp_FPUBranch(u32 op) js.compiling = false; } +// If likely is set, discard the branch slot if NOT taken. +void Jit::BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely) +{ + int offset = (signed short)(op & 0xFFFF) << 2; + u32 targetAddr = js.compilerPC + offset + 4; + + u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); + + bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp); + if (!delaySlotIsNice) + { + ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :("); + } + FlushAll(); + + ARMABI_MOVI2R(R0, (u32)&(mips_->vfpuCtrl[VFPU_CTRL_CC])); + LDR(R0, R0, Operand2(0, TYPE_IMM)); + + int imm3 = (op >> 18) & 7; + TST(R0, Operand2(1 << imm3, TYPE_IMM)); + + ArmGen::FixupBranch ptr; + js.inDelaySlot = true; + if (!likely) + { + MRS(R0); // Save flags register + PUSH(1, R0); + + CompileAt(js.compilerPC + 4); + FlushAll(); + + // POPF(); // restore flag! + POP(1, R0); + _MSR(true, false, R0); // Restore flags register + ptr = B_CC(cc); + } + else + { + ptr = B_CC(cc); + CompileAt(js.compilerPC + 4); + FlushAll(); + } + js.inDelaySlot = false; + + // Take the branch + WriteExit(targetAddr, 0); + + SetJumpTarget(ptr); + // Not taken + WriteExit(js.compilerPC + 8, 1); + js.compiling = false; +} + void Jit::Comp_VBranch(u32 op) { - /* - Comp_Generic(op + 4); - Comp_Generic(op); - js.compiling = false; - return; - - int imm = (signed short)(op&0xFFFF)<<2; - u32 targetAddr = js.compilerPC + imm + 4; - - int imm3 = (op >> 18) & 7; - int val = (mips_->vfpuCtrl[VFPU_CTRL_CC] >> imm3) & 1; - switch ((op >> 16) & 3) { - //case 0: if (!val) DelayBranchTo(addr); else PC += 4; break; //bvf - //case 1: if ( val) DelayBranchTo(addr); else PC += 4; break; //bvt - //case 2: if (!val) DelayBranchTo(addr); else PC += 8; break; //bvfl - //case 3: if ( val) DelayBranchTo(addr); else PC += 8; break; //bvtl - //TODO - } - */ + case 0: BranchVFPUFlag(op, CC_NEQ, false); break; // bvf + case 1: BranchVFPUFlag(op, CC_EQ, false); break; // bvt + case 2: BranchVFPUFlag(op, CC_NEQ, true); break; // bvfl + case 3: BranchVFPUFlag(op, CC_EQ, true); break; // bvtl + } js.compiling = false; } diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 9dc526d59d..4f63c905bc 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -105,6 +105,7 @@ void Jit::RunLoopUntil(u64 globalticks) { // TODO: copy globalticks somewhere ((void (*)())asm_.enterCode)(); + INFO_LOG(DYNA_REC, "Left asm code"); } const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 8d9f1de9ee..277ccb5fa4 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -88,7 +88,7 @@ public: void Comp_mxc1(u32 op); ArmJitBlockCache *GetBlockCache() { return &blocks; } - AsmRoutineManager &Asm() { return asm_; } + ArmAsmRoutineManager &Asm() { return asm_; } void ClearCache(); @@ -103,6 +103,7 @@ private: // Utility compilation functions void BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely); + void BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely); void BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely); void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely); @@ -123,7 +124,7 @@ private: ArmRegCache gpr; // FPURegCache fpr; - AsmRoutineManager asm_; + ArmAsmRoutineManager asm_; MIPSState *mips_; }; diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 83037335df..575e6ad8e1 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -45,7 +45,7 @@ void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { static const ARMReg *GetMIPSAllocationOrder(int &count) { // Note that R0 and R1 are reserved as scratch for now. We can probably free up R1 eventually. static const ARMReg allocationOrder[] = { - R2, R3, R4, R5, R6, R7, R8, R9 + R2, R3, R4, R5, R6, R7, R8 }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; diff --git a/Core/MIPS/x86/Asm.h b/Core/MIPS/x86/Asm.h index d60aa4172d..b20811a7fe 100644 --- a/Core/MIPS/x86/Asm.h +++ b/Core/MIPS/x86/Asm.h @@ -38,6 +38,10 @@ public: AsmRoutineManager() { } + ~AsmRoutineManager() + { + FreeCodeSpace(); + } void Init(MIPSState *mips, MIPSComp::Jit *jit) { @@ -45,10 +49,6 @@ public: Generate(mips, jit); WriteProtect(); } - ~AsmRoutineManager() - { - FreeCodeSpace(); - } const u8 *enterCode; diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index 0dffb2548f..c562821a19 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -47,10 +47,6 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) CoreParameter coreParam; coreParam.cpuCore = (CPUCore)g_Config.iCpuCore; -#if defined(ARM) - if (coreParam.cpuCore == CPU_JIT) - coreParam.cpuCore = CPU_FASTINTERPRETER; -#endif coreParam.gpuCore = GPU_GLES; coreParam.enableSound = g_Config.bEnableSound; coreParam.fileToStart = fileToStart; diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index 2994000f8c..73c6da8fc0 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -285,19 +285,14 @@ void SettingsScreen::render() { } UICheckBox(GEN_ID, x, y += 45, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); - bool useFastInt = g_Config.iCpuCore == CPU_FASTINTERPRETER; - UICheckBox(GEN_ID, x, y += 45, "Slightly faster interpreter (may crash)", ALIGN_TOPLEFT, &useFastInt); + bool useJit = g_Config.iCpuCore == CPU_JIT; + UICheckBox(GEN_ID, x, y += 45, "Use Dynarec (JIT)", ALIGN_TOPLEFT, &useJit); + g_Config.iCpuCore = useJit ? CPU_JIT : CPU_INTERPRETER; // ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x, y+=50, 0xcFFFFFFF, ALIGN_LEFT); UICheckBox(GEN_ID, x, y += 45, "On-screen Touch Controls", ALIGN_TOPLEFT, &g_Config.bShowTouchControls); if (g_Config.bShowTouchControls) UICheckBox(GEN_ID, x + 50, y += 50, "Show Analog Stick", ALIGN_TOPLEFT, &g_Config.bShowAnalogStick); - g_Config.iCpuCore = useFastInt ? CPU_FASTINTERPRETER : CPU_INTERPRETER; - bool useJit = g_Config.iCpuCore == CPU_JIT; - UICheckBox(GEN_ID, x, y += 50, "Use JIT (very crashy)", ALIGN_TOPLEFT, &useJit); - if (useJit) { - g_Config.iCpuCore = CPU_JIT; - } // UICheckBox(GEN_ID, x, y += 50, "Draw raw framebuffer (for some homebrew)", ALIGN_TOPLEFT, &g_Config.bDisplayFramebuffer); if (UIButton(GEN_ID, Pos(dp_xres - 10, dp_yres-10), LARGE_BUTTON_WIDTH, "Back", ALIGN_RIGHT | ALIGN_BOTTOM)) { diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index bd7cd15d12..3c990c7aa9 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity { { return false; } -} \ No newline at end of file +} \ No newline at end of file From b3fd1ff34c7c0b44389d1a7400be0e947cfee34f Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 13:49:52 +0100 Subject: [PATCH 08/49] Lots of various work on the ARM jit. It executes a couple of blocks now. --- Common/ArmABI.cpp | 1 - Common/ArmEmitter.cpp | 3 +- Core/Core.vcxproj | 2 + Core/Core.vcxproj.filters | 6 + Core/MIPS/ARM/ArmAsm.cpp | 16 +- Core/MIPS/ARM/ArmCompALU.cpp | 76 +-- Core/MIPS/ARM/ArmCompBranch.cpp | 56 +- Core/MIPS/ARM/ArmJit.cpp | 99 ++-- Core/MIPS/ARM/ArmJit.h | 3 + Core/MIPS/ARM/ArmRegCache.cpp | 20 +- Core/MIPS/MIPSTables.cpp | 14 +- Core/MIPS/MIPSTables.h | 2 +- Core/MIPS/x86/Asm.cpp | 3 - Core/MemMapFunctions.cpp | 2 +- android/jni/Android.mk | 1 + ext/disarm.cpp | 930 ++++++++++++++++++++++++++++++++ ext/disarm.h | 33 ++ 17 files changed, 1137 insertions(+), 130 deletions(-) create mode 100644 ext/disarm.cpp create mode 100644 ext/disarm.h diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index fa86924dfb..b23f72aeab 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -35,7 +35,6 @@ void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) ARMABI_MOVI2R(R0, Arg); BL(R14); POP(5, R0, R1, R2, R3, _LR); - } void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) { diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index ad54667dec..adcf07cb94 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -405,8 +405,7 @@ void ARMXEmitter::DMB () void ARMXEmitter::LDR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x41, src, dest, op);} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);} -void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, -bool Add) +void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); } diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index 610f37f31a..142f3b401a 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -116,6 +116,7 @@ + @@ -268,6 +269,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index 03ca3df852..c7baaebccd 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -366,6 +366,9 @@ MIPS\ARM + + Ext + @@ -677,6 +680,9 @@ MIPS\ARM + + Ext + diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 91139a8b18..9df0a58f37 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -62,6 +62,7 @@ extern volatile CoreState coreState; void Jit() { + INFO_LOG(HLE, "Compiling at %08x", currentMIPS->pc); MIPSComp::jit->Compile(currentMIPS->pc); } @@ -70,10 +71,10 @@ void ImHere() { i++; INFO_LOG(HLE, "I'm too here %i", i); } -void ImHere2(u32 hej) { +void ImHere2(u32 hej, u32 hej2) { static int i = 0; i++; - INFO_LOG(HLE, "I'm here2 %i %08x", i, hej); + INFO_LOG(HLE, "I'm here2 %i %08x %08x", i, hej, hej2); } // PLAN: no more block numbers - crazy opcodes just contain offset within // dynarec buffer @@ -83,6 +84,8 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) { enterCode = AlignCode16(); + INFO_LOG(HLE, "Base: %08x", (u32)Memory::base); + SetCC(CC_AL); PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); @@ -126,9 +129,8 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) ARMABI_MOVI2R(R1, Memory::MEMVIEW32_MASK); // can be done with single MOVN instruction AND(R0, R0, R1); - ARMABI_CallFunction((void *)&ImHere2); - - LDR(R0, R11, R(R0)); + ADD(R0, R0, R11); // TODO: Optimize (can merge with next instr) + LDR(R0, R0); AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); @@ -139,14 +141,12 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //ADD(32, M(&mips->debugCount), Imm8(1)); } // grab from list and jump to it - ADD(R0, R14, Operand2(2, ST_LSL, R0)); + ADD(R0, R9, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); SetJumpTarget(notfound); - ARMABI_CallFunction((void *)&ImHere); - //Ok, no block, let's jit ARMABI_CallFunction((void *)&Jit); diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 459e5effa9..d467f01d1c 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -35,9 +35,10 @@ using namespace MIPSAnalyst; namespace MIPSComp { /* - void Jit::CompImmLogic(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &)) + void Jit::CompImmLogic(u32 op, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2)) { u32 uimm = (u16)(op & 0xFFFF); + gpr.SpillLock() int rt = _RT; int rs = _RS; gpr.Lock(rt, rs); @@ -46,14 +47,10 @@ namespace MIPSComp MOV(32, gpr.R(rt), gpr.R(rs)); (this->*arith)(32, gpr.R(rt), Imm32(uimm)); gpr.UnlockAll(); - - } - */ + }*/ void Jit::Comp_IType(u32 op) { - OLDD - /* s32 simm = (s16)(op & 0xFFFF); u32 uimm = (u16)(op & 0xFFFF); @@ -65,30 +62,47 @@ namespace MIPSComp case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { - if (gpr.R(rs).IsImm()) + if (gpr.IsImm(rs)) { - gpr.SetImmediate32(rt, gpr.R(rs).GetImmValue() + simm); + gpr.SetImm(rt, gpr.GetImm(rs) + simm); break; + } else if (rs == 0) { + gpr.SetImm(rt, simm); + } else { + gpr.SpillLock(rs, rt); + gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); + ARMABI_MOVI2R(R0, (u32)simm); + ADD(gpr.R(rt), gpr.R(rs), R0); + gpr.ReleaseSpillLocks(); } - - gpr.Lock(rt, rs); - if (rs != 0) + break; + } +/* + case 13: // OR + { + if (gpr.IsImm(rs)) { - gpr.BindToRegister(rt, rt == rs, true); - if (rt != rs) - MOV(32, gpr.R(rt), gpr.R(rs)); - if (simm != 0) - ADD(32, gpr.R(rt), Imm32((u32)(s32)simm)); - // TODO: Can also do LEA if both operands happen to be in registers. + gpr.SetImm(rt, gpr.GetImm(rs) | uimm); + break; + } else if (rs == 0) { + gpr.SetImm(rt, uimm); + } else { + gpr.SpillLock(rs, rt); + gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); + ARMABI_MOVI2R(R0, (u32)uimm); + ORR(gpr.R(rt), gpr.R(rs), R0); + gpr.ReleaseSpillLocks(); } - else - { - gpr.SetImmediate32(rt, simm); - } - gpr.UnlockAll(); + break; } break; + */ + //case 12: CompImmLogic(op, &XEmitter::AND); break; + //case 14: CompImmLogic(op, &XEmitter::XOR); break; + /* case 10: // R(rt) = (s32)R(rs) < simm; break; //slti gpr.Lock(rt, rs); gpr.BindToRegister(rt, rt == rs, true); @@ -109,19 +123,16 @@ namespace MIPSComp gpr.UnlockAll(); break; - case 12: CompImmLogic(op, &XEmitter::AND); break; - case 13: CompImmLogic(op, &XEmitter::OR); break; - case 14: CompImmLogic(op, &XEmitter::XOR); break; + */ case 15: //R(rt) = uimm << 16; break; //lui - gpr.SetImmediate32(rt, uimm << 16); + gpr.SetImm(rt, uimm << 16); break; default: Comp_Generic(op); break; - }*/ - + } } //rd = rs X rt @@ -146,12 +157,15 @@ namespace MIPSComp { OLDD - int rt = _RT; int rs = _RS; int rd = _RD; - // gpr.Lock(rd, rs, rt); + gpr.SpillLock(rt, rs, rd); + gpr.MapReg(rt, MAP_INITVAL); + gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rd, MAP_INITVAL | MAP_DIRTY); // can get rid of INITVAL in some cases + gpr.ReleaseSpillLocks(); switch (op & 63) { @@ -210,8 +224,6 @@ namespace MIPSComp Comp_Generic(op); break; } - // gpr.UnlockAll(); - } /* diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 799bbf94f2..19a557e4cd 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -58,13 +58,22 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC); } // The delay slot being nice doesn't really matter though... - - if (rs == 0) + if (rt == 0) { + gpr.MapReg(rs, MAP_INITVAL); CMP(gpr.R(rs), Operand2(0)); } - else - { + else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' + { + gpr.MapReg(rt, MAP_INITVAL); + CMP(gpr.R(rt), Operand2(0)); + } + else + { + gpr.SpillLock(rs, rt); + gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rt, MAP_INITVAL); + gpr.ReleaseSpillLocks(); CMP(gpr.R(rs), gpr.R(rt)); } FlushAll(); @@ -76,12 +85,10 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. - MRS(R0); // Save flags register - PUSH(1, R0); + MRS(R8); // Save flags register. R4 is preserved through function calls and is not allocated. CompileAt(js.compilerPC + 4); FlushAll(); - POP(1, R0); - _MSR(true, false, R0); // Restore flags register + _MSR(true, false, R8); // Restore flags register ptr = B_CC(cc); } else @@ -116,7 +123,7 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) { // ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC); } - + gpr.MapReg(rs, MAP_INITVAL); CMP(gpr.R(rs), Operand2(0)); FlushAll(); @@ -127,13 +134,10 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. - MRS(R0); // Save flags register - PUSH(1, R0); + MRS(R8); // Save flags register CompileAt(js.compilerPC + 4); FlushAll(); - - POP(1, R0); - _MSR(true, false, R0); // Restore flags register + _MSR(true, false, R8); // Restore flags register ptr = B_CC(cc); } else @@ -215,15 +219,12 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) js.inDelaySlot = true; if (!likely) { - MRS(R0); // Save flags register - PUSH(1, R0); + MRS(R8); // Save flags register CompileAt(js.compilerPC + 4); FlushAll(); - // POPF(); // restore flag! - POP(1, R0); - _MSR(true, false, R0); // Restore flags register + _MSR(true, false, R8); // Restore flags register ptr = B_CC(cc); } else @@ -283,15 +284,12 @@ void Jit::BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely) js.inDelaySlot = true; if (!likely) { - MRS(R0); // Save flags register - PUSH(1, R0); + MRS(R8); // Save flags register CompileAt(js.compilerPC + 4); FlushAll(); - // POPF(); // restore flag! - POP(1, R0); - _MSR(true, false, R0); // Restore flags register + _MSR(true, false, R8); // Restore flags register ptr = B_CC(cc); } else @@ -356,24 +354,24 @@ void Jit::Comp_JumpReg(u32 op) u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); bool delaySlotIsNice = GetOutReg(delaySlotOp) != _RS; // Do what with that information? + int rs = _RS; - ARMReg rs = gpr.R(_RS); + gpr.MapReg(rs, MAP_INITVAL); // Delay slot - PUSH(1, rs); // Save the destination address through the delay slot. Could use isNice to avoid + MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid CompileAt(js.compilerPC + 4); FlushAll(); - POP(1, R0); + MOV(R0, R8); // TODO: Remove. switch (op & 0x3f) { case 8: //jr break; case 9: //jalr - ARMABI_MOVI2R(R1, (u32)&mips_->r[MIPS_REG_RA]); + ADD(R1, R9, MIPS_REG_RA * 4); // compute address of RA in ram ARMABI_MOVI2R(R2, js.compilerPC + 8); STR(R1, R2); - // MOV(32, M(&mips_->r[MIPS_REG_RA]), Imm32(js.compilerPC + 8)); break; default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 4f63c905bc..e15a78f3ca 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -25,39 +25,10 @@ #include "ArmRegCache.h" #include "ArmJit.h" +#include "../../ext/disarm.h" + namespace MIPSComp { -/* -void Comp_MemRead32KnownRAM(int srcreg, int offset, int reg) -{ - MOV_MemoryToReg(1, reg, ModRM_disp32_EAX+srcreg, (u32)pspmainram - 0x08000000 + offset); -} -void Comp_MemWrite32KnownRAM(int srcreg, int offset, int reg) -{ - MOV_RegToMemory(1, reg, ModRM_disp32_EAX+srcreg, (u32)pspmainram - 0x08000000 + offset); -} - -#define RM(reg) (&(currentMIPS->r[reg])) -struct RegInfo -{ - bool cached; - int x86reg; -}; - -RegInfo regs[32]; - -void MovToReg(int reg, u32 value) -{ - if (regs[reg].cached) - { - ::MOV_ImmToReg(1,regs[reg].x86reg,value,0); - } - else - { - ::MOV_ImmToMemory(1,ModRM_disp32,(u32)(RM(reg)),value); - } -} -*/ Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips) { @@ -108,6 +79,10 @@ void Jit::RunLoopUntil(u64 globalticks) INFO_LOG(DYNA_REC, "Left asm code"); } +void Hullo(int a, int b, int c, int d) { + INFO_LOG(DYNA_REC, "Hullo %08x %08x %08x %08x", a, b, c, d); +} + const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { js.cancel = false; @@ -116,20 +91,36 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) js.curBlock = b; js.compiling = true; js.inDelaySlot = false; - - b->normalEntry = GetCodePtr(); + // We add a check before the block, used when entering from a linked block. + b->checkedEntry = GetCodePtr(); + // Downcount flag check. The last block decremented downcounter, and the flag should still be available. + SetCC(CC_LT); + ARMABI_MOVI2R(R0, js.blockStart); + MovToPC(R0); + ARMABI_MOVI2R(R0, (u32)asm_.outerLoop); + B(R0); + SetCC(CC_AL); + + b->normalEntry = GetCodePtr(); // TODO: this needs work MIPSAnalyst::AnalysisResults analysis; // = MIPSAnalyst::Analyze(em_address); gpr.Start(analysis); - //fpr.Start(mips_, analysis); int numInstructions = 0; int cycles = 0; +#define LOGASM +#ifdef LOGASM + char temp[256]; +#endif while (js.compiling) { u32 inst = Memory::Read_Instruction(js.compilerPC); +#ifdef LOGASM + MIPSDisAsm(inst, js.compilerPC, temp, true); + INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); +#endif js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst); @@ -137,8 +128,21 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) js.compilerPC += 4; numInstructions++; } - +#ifdef LOGASM + MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); + INFO_LOG(DYNA_REC, "M: %s", temp); +#endif + b->codeSize = GetCodePtr() - b->normalEntry; +#ifdef LOGASM + for (int i = 0; i < b->codeSize; i += 4) { + const u32 *codePtr = (const u32 *)(b->normalEntry + i); + u32 inst = *codePtr; + ArmDis((u32)codePtr, inst, temp); + INFO_LOG(DYNA_REC, "A: %s", temp); + } +#endif + NOP(); AlignCode16(); b->originalSize = numInstructions; @@ -157,11 +161,20 @@ void Jit::Comp_Generic(u32 op) MIPSInterpretFunc func = MIPSGetInterpretFunc(op); if (func) { - //MOV(32, M(&mips_->pc), Imm32(js.compilerPC)); - //ABI_CallFunctionC(func, op); + ARMABI_MOVI2R(R0, js.compilerPC); + MovToPC(R0); + ARMABI_CallFunctionC((void *)func, op); } } +void Jit::MovFromPC(ARMReg r) { + LDR(r, R9, offsetof(MIPSState, pc)); +} + +void Jit::MovToPC(ARMReg r) { + STR(R9, r, offsetof(MIPSState, pc)); +} + void Jit::DoDownCount() { ARMABI_MOVI2R(R0, Mem(&CoreTiming::downcount)); @@ -181,9 +194,9 @@ void Jit::DoDownCount() void Jit::WriteExitDestInR(ARMReg Reg) { - ARMABI_MOVI2R(R0, (u32)&mips_->pc); - STR(R0, Reg); + MovToPC(Reg); DoDownCount(); + // TODO: shouldn't need an indirect branch here... ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); B(R0); } @@ -206,9 +219,8 @@ void Jit::WriteExit(u32 destination, int exit_num) } else { - ARMABI_MOVI2R(R0, (u32)&mips_->pc); // Watch out! This uses R14 and R12! - ARMABI_MOVI2R(R1, destination); // Watch out! This uses R14 and R12! - STR(R0, R1); // Watch out! This uses R14 and R12! + ARMABI_MOVI2R(R0, destination); + MovToPC(R0); ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); B(R0); } @@ -216,9 +228,8 @@ void Jit::WriteExit(u32 destination, int exit_num) void Jit::WriteSyscallExit() { - // Super basic DoDownCount(); - B((const void *)asm_.dispatcher); + B((const void *)asm_.dispatcherCheckCoreState); } diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 277ccb5fa4..4f70ea53ed 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -96,6 +96,8 @@ private: void FlushAll(); void DoDownCount(); + void MovFromPC(ARMReg r); + void MovToPC(ARMReg r); void WriteExit(u32 destination, int exit_num); void WriteExitDestInR(ARMReg Reg); @@ -109,6 +111,7 @@ private: // Utilities to reduce duplicated code /* + void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2)); void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompTriArith(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompShiftImm(u32 op, void (ARMXEmitter::*shift)(int, OpArg, OpArg)); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 575e6ad8e1..49b4f5fd05 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -21,7 +21,7 @@ using namespace ArmGen; -#define CTXREG ((ARMReg)14) +#define CTXREG ((ARMReg)9) ArmRegCache::ArmRegCache(MIPSState *mips) : mips_(mips) { } @@ -44,8 +44,11 @@ void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { static const ARMReg *GetMIPSAllocationOrder(int &count) { // Note that R0 and R1 are reserved as scratch for now. We can probably free up R1 eventually. + // R8 is used to preserve flags in nasty branches. + // R9 and upwards are reserved for jit basics. + // Six allocated registers should be enough... static const ARMReg allocationOrder[] = { - R2, R3, R4, R5, R6, R7, R8 + R2, R3, R4, R5, R6, R7 }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; @@ -55,6 +58,8 @@ ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { // Let's see if it's already mapped. for (int i = 0; i < NUM_ARMREG; i++) { if (ar[i].mipsReg == mipsReg) { + if (mapFlags & MAP_DIRTY) + ar[i].isDirty = true; // Already mapped, no need to do anything more. return (ARMReg)i; } @@ -79,6 +84,8 @@ allocate: else if (mr[mipsReg].loc == ML_IMM) emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm); } + mr[mipsReg].loc = ML_ARMREG; + mr[mipsReg].reg = (ARMReg)reg; return (ARMReg)reg; } } @@ -104,7 +111,9 @@ void ArmRegCache::FlushArmReg(ARMReg r) { } if (ar[r].isDirty) { if (mr[ar[r].mipsReg].loc == ML_MEM) - emit->STR(r, CTXREG, 4 * ar[r].mipsReg); + emit->STR(CTXREG, r, 4 * ar[r].mipsReg); + ar[r].isDirty = false; + ar[r].mipsReg = -1; } } @@ -113,12 +122,12 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { case ML_IMM: // IMM is always "dirty". emit->ARMABI_MOVI2R(R0, mr[r].imm); - emit->STR(R0, CTXREG, GetMipsRegOffset(r)); + emit->STR(CTXREG, R0, GetMipsRegOffset(r)); break; case ML_ARMREG: if (ar[mr[r].reg].isDirty) - emit->STR(mr[r].reg, CTXREG, GetMipsRegOffset(r)); + emit->STR(CTXREG, mr[r].reg, GetMipsRegOffset(r)); ar[mr[r].reg].mipsReg = -1; ar[mr[r].reg].isDirty = false; break; @@ -183,6 +192,7 @@ ARMReg ArmRegCache::R(int mipsReg) { return mr[mipsReg].reg; } else { _dbg_assert_msg_(JIT, false, "R: not mapped"); + ERROR_LOG(JIT, "Reg %i not in arm reg", mipsReg); return INVALID_REG; // BAAAD } } diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index f6b4bdc3e3..47582c2ccc 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -885,7 +885,7 @@ void MIPSCompileOp(u32 op) } -void MIPSDisAsm(u32 op, u32 pc, char *out) +void MIPSDisAsm(u32 op, u32 pc, char *out, bool tabsToSpaces) { if (op == 0) { @@ -896,10 +896,16 @@ void MIPSDisAsm(u32 op, u32 pc, char *out) { disPC = pc; const MIPSInstruction *instr = MIPSGetInstruction(op); - if (instr && instr->disasm) + if (instr && instr->disasm) { instr->disasm(op, out); - else - { + if (tabsToSpaces) { + while (*out) { + if (*out == '\t') + *out = ' '; + out++; + } + } + } else { strcpy(out, "no instruction :("); //__asm int 3 MIPSGetInstruction(op); diff --git a/Core/MIPS/MIPSTables.h b/Core/MIPS/MIPSTables.h index 7f71735236..753e7dedc3 100644 --- a/Core/MIPS/MIPSTables.h +++ b/Core/MIPS/MIPSTables.h @@ -53,7 +53,7 @@ typedef void (CDECL *MIPSInterpretFunc)(u32 opcode); void MIPSCompileOp(u32 op); -void MIPSDisAsm(u32 op, u32 pc, char *out); +void MIPSDisAsm(u32 op, u32 pc, char *out, bool tabsToSpaces = false); u32 MIPSGetInfo(u32 op); void MIPSInterpret(u32 op); //only for those rare ones int MIPSInterpret_RunFastUntil(u64 globalTicks); diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 715d8bef10..c0c0b9f64f 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -93,9 +93,6 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) dispatcherNoCheck = GetCodePtr(); - // Debug - // CALL(&ImHere); - MOV(32, R(EAX), M(&mips->pc)); #ifdef _M_IX86 AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index faa0e5fa18..ec24911f76 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -83,7 +83,7 @@ inline void ReadFromHardware(T &var, const u32 address) } else { - WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); + WARN_LOG(MEMMAP, "ReadFromHardware: Invalid address %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); if (!g_Config.bIgnoreBadMemAccess) { Core_EnableStepping(true); host->SetDebugMode(true); diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 4e54d3bcff..22b0b2f498 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -49,6 +49,7 @@ LOCAL_SRC_FILES := \ ArmEmitterTest.cpp \ ui_atlas.cpp \ $(SRC)/native/android/app-android.cpp \ + $(SRC)/ext/disarm.cpp \ $(SRC)/ext/libkirk/AES.c \ $(SRC)/ext/libkirk/SHA1.c \ $(SRC)/ext/libkirk/bn.c \ diff --git a/ext/disarm.cpp b/ext/disarm.cpp new file mode 100644 index 0000000000..f702ebf943 --- /dev/null +++ b/ext/disarm.cpp @@ -0,0 +1,930 @@ +/* disarm -- a simple disassembler for ARM instructions + * (c) 2000 Gareth McCaughan + * + * This file may be distributed and used freely provided: + * 1. You do not distribute any version that lacks this + * copyright notice (exactly as it appears here, extending + * from the start to the end of the C-language comment + * containing these words)); and, + * 2. If you distribute any modified version, its source + * contains a clear description of the ways in which + * it differs from the original version, and a clear + * indication that the changes are not mine. + * There is no restriction on your permission to use and + * distribute object code or executable code derived from + * this. + * + * The original version of this file (or perhaps a later + * version by the original author) may or may not be + * available at http://web.ukonline.co.uk/g.mccaughan/g/software.html . + * + * Share and enjoy! -- g + */ + +/* (*This* comment is NOT part of the notice mentioned in the + * distribution conditions above.) + * + * The bulk of this code was ripped brutally from the middle + * of a much more interesting piece of software whose purpose + * is to disassemble object files in the format known as AOF; + * it's quite clever at spotting blocks of non-code embedded + * in code, identifying labels, and so on. + * + * This program, on the other hand, is very much simpler. + * It simply disassembles one instruction at a time. Some + * traces of the original purpose can be seen here and there. + * You might want to make this do a two-phase disassembly, + * adding labels etc the second time around. I've made this + * work by loading the whole file into memory first, partly + * because that makes a two-pass approach easier. + * + * One word of warning: I believe that the syntax this program + * uses for the MSR instruction is now obsolete. + * + * Usage: + * disarm + * will disassemble every word in . + * + * should be something understood by strtol. + * So you can get hex (which is probably what you want) + * by prefixing "0x". + * + * The -r option will byte-reverse each word before it's + * disassembled. + * + * The code is rather unmaintainable. I'm sorry. + * + * Changes since original release: + * ????-??-?? v0.00 Initial release. + * 2007-09-02 v0.11 Change %X to %lX in a format string. + * (Thanks to Vincent Zweije for reporting this.) + */ + +#include +#include +#include + +typedef unsigned int word; +typedef unsigned int address; +typedef unsigned int addrdiff; +#define W(x) ((word*)(x)) + +#define declstruct(name) typedef struct name s##name, * p##name +#define defstruct(name) struct name +#define defequiv(new,old) typedef struct old s##new, * p##new + +declstruct(DisOptions); +declstruct(Instruction); + +typedef enum { + target_None, /* instruction doesn't refer to an address */ + target_Data, /* instruction refers to address of data */ + target_FloatS, /* instruction refers to address of single-float */ + target_FloatD, /* instruction refers to address of double-float */ + target_FloatE, /* blah blah extended-float */ + target_FloatP, /* blah blah packed decimal float */ + target_Code, /* instruction refers to address of code */ + target_Unknown /* instruction refers to address of *something* */ +} eTargetType; + +defstruct(Instruction) { + char text[128]; /* the disassembled instruction */ + int undefined; /* non-0 iff it's an undefined instr */ + int badbits; /* non-0 iff something reserved has the wrong value */ + int oddbits; /* non-0 iff something unspecified isn't 0 */ + int is_SWI; /* non-0 iff it's a SWI */ + word swinum; /* only set for SWIs */ + address target; /* address instr refers to */ + eTargetType target_type; /* and what we expect to be there */ + int offset; /* offset from register in LDR or STR or similar */ + char * addrstart; /* start of address part of instruction, or 0 */ +}; + +#define disopt_SWInames 1 /* use names, not &nnnn */ +#define disopt_CommaSpace 2 /* put spaces after commas */ +#define disopt_FIXS 4 /* bogus FIX syntax for ObjAsm */ +#define disopt_ReverseBytes 8 /* byte-reverse words first */ + +defstruct(DisOptions) { + word flags; + const char * * regnames; /* pointer to 16 |char *|s: register names */ +}; + +static pInstruction instr_disassemble(word, address, pDisOptions); + +#define INSTR_grok_v4 + +/* Preprocessor defs you can give to affect this stuff: + * INSTR_grok_v4 understand ARMv4 instructions (halfword & sign-ext LDR/STR) + * INSTR_new_msr be prepared to produce new MSR syntax if asked + * The first of these is supported; the second isn't. + */ + +/* Some important single-bit fields. */ + +#define Sbit (1<<20) /* set condition codes (data processing) */ +#define Lbit (1<<20) /* load, not store (data transfer) */ +#define Wbit (1<<21) /* writeback (data transfer) */ +#define Bbit (1<<22) /* single byte (data transfer, SWP) */ +#define Ubit (1<<23) /* up, not down (data transfer) */ +#define Pbit (1<<24) /* pre-, not post-, indexed (data transfer) */ +#define Ibit (1<<25) /* non-immediate (data transfer) */ + /* immediate (data processing) */ +#define SPSRbit (1<<22) /* SPSR, not CPSR (MRS, MSR) */ + +/* Some important 4-bit fields. */ + +#define RD(x) ((x)<<12) /* destination register */ +#define RN(x) ((x)<<16) /* operand/base register */ +#define CP(x) ((x)<<8) /* coprocessor number */ +#define RDbits RD(15) +#define RNbits RN(15) +#define CPbits CP(15) +#define RD_is(x) ((instr&RDbits)==RD(x)) +#define RN_is(x) ((instr&RNbits)==RN(x)) +#define CP_is(x) ((instr&CPbits)==CP(x)) + +/* A slightly efficient way of telling whether two bits are the same + * or not. It's assumed that a>(b-a)))&(1<=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15]; + return op; +} + +/* op = reg(op,'x',n) === op += sprintf(op,"x%lu",n&15). + */ +static char * reg(char * op, char c, word n) { + *op++=c; + n&=15; + if (n>=10) { *op++='1'; n+='0'-10; } else n+='0'; + *op++=(char)n; + return op; +} + +/* op = num(op,n) appends n in decimal or &n in hex + * depending on whether n<100. It's assumed that n>=0. + */ +static char * num(char * op, word w) { + if (w>=100) { + int i; + word t; + *op++='&'; + for (i=28; (t=(w>>i)&15)==0; i-=4) ; + for (; i>=0; i-=4) *op++ = "0123456789ABCDEF"[(w>>i)&15]; + } + else { + /* divide by 10. You can prove this works by exhaustive search. :-) */ + word t = w-(w>>2); t=(t+(t>>4)) >> 3; + { word u = w-10*t; + if (u==10) { u=0; ++t; } + if (t) *op++=(char)(t+'0'); + *op++=(char)(u+'0'); + } + } + return op; +} + +/* instr_disassemble + * Disassemble a single instruction. + * + * args: instr a single ARM instruction + * addr the address it's presumed to have come from + * opts cosmetic preferences for our output + * + * reqs: opts must be filled in right. In particular, it must contain + * a list of register names. + * + * return: a pointer to a structure containing the disassembled instruction + * and some other information about it. + * + * This is basically a replacement for the SWI Debugger_Disassemble, + * but it has the following advantages: + * + * + it's 3-4 times as fast + * + it's better at identifying undefined instructions, + * and instructions not invariant under { disassemble; ObjAsm; } + * + it provides some other useful information as well + * + its output syntax is the same as ObjAsm's input syntax + * (where possible) + * + it doesn't disassemble FIX incorrectly unless you ask it to + * + it's more configurable in some respects + * + * It also has the following disadvantages: + * + * - it increases the size of ObjDism + * - it doesn't provide so many `helpful' usage comments etc + * - it's less configurable in some respects + * - it doesn't (yet) know about ARMv4 instructions + * + * This function proceeds in two phases. The first is very simple: + * it works out what sort of instruction it's looking at and sets up + * three strings: + * - |mnemonic| (the basic mnemonic: LDR or whatever) + * - |flagchars| (things to go after the cond code: B or whatever) + * - |format| (a string describing how to display the instruction) + * The second phase consists of interpreting |format|, character by + * character. Some characters (e.g., letters) just mean `append this + * character to the output string'; some mean more complicated things + * like `append the name of the register whose number is in bits 12..15' + * or, worse, `append a description of the field'. + * + * I'm afraid the magic characters in |format| are rather arbitrary. + * One criterion in choosing them was that they should form a contiguous + * subrange of the character set! Sorry. + * + * Things I still want to do: + * + * - more configurability? + * - make it much faster, if possible + * - make it much smaller, if possible + * + * Format characters: + * + * \01..\05 copro register number from nybble (\001 == nybble 0, sorry) + * $ SWI number + * % register set for LDM/STM (takes note of bit 22 for ^) + * & address for B/BL + * ' ! if bit 21 set, else nothing (mnemonic: half a !) + * ( #regs for SFM (bits 22,15 = fpn, assumed already tweaked) + * ) copro opcode in bits 20..23 (for CDP) + * * op2 (takes note of bottom 12 bits, and bit 25) + * + FP register or immediate value: bits 0..3 + * , comma or comma-space + * - copro extra info in bits 5..7 preceded by , omitted if 0 + * . address in ADR instruction + * / address for LDR/STR (takes note of bit 23 & reg in bits 16..19) + * 0..4 register number from nybble + * 5..9 FP register number from nybble + * : copro opcode in bits 21..23 (for MRC/MCR) + * ; copro number in bits 8..11 + * + * NB that / takes note of bit 22, too, and does its own ! when + * appropriate. + * + * On typical instructions this seems to take about 100us on my ARM6; + * that's about 3000 cycles, which seems grossly excessive. I'm not + * sure where all those cycles are being spent. Perhaps it's possible + * to make it much, much faster. Most of this time is spent on phase 2. + */ + +extern pInstruction +instr_disassemble(word instr, address addr, pDisOptions opts) { + static char flagchars[4]; + static sInstruction result; + const char * mnemonic = 0; + char * flagp = flagchars; + const char * format = 0; + word fpn; + eTargetType poss_tt = target_None; +#ifdef INSTR_grok_v4 + int is_v4 = 0; +#endif + + /* PHASE 0. Set up default values for |result|. */ + + if (opts->flags & disopt_ReverseBytes) { + instr = ((instr & 0xFF00FF00) >> 8) | ((instr & 0x00FF00FF) << 8); + instr = (instr >> 16) | (instr << 16); + } + + fpn = ((instr>>15)&1) + ((instr>>21)&2); + + result.undefined = + result.badbits = + result.oddbits = + result.is_SWI = 0; + result.target_type = target_None; + result.offset = 0x80000000; + result.addrstart = 0; + + /* PHASE 1. Decode and classify instruction. */ + + switch ((instr>>24)&15) { + case 0: + /* multiply or data processing, or LDRH etc */ + if ((instr&(15<<4))!=(9<<4)) goto lMaybeLDRHetc; + /* multiply */ + if (instr&(1<<23)) { + /* int multiply */ + mnemonic = "UMULL\0UMLAL\0SMULL\0SMLAL" + 6*((instr>>21)&3); + format = "3,4,0,2"; + } + else { + if (instr&(1<<22)) goto lUndefined; /* "class C" */ + /* short multiply */ + if (instr&(1<<21)) { + mnemonic = "MLA"; + format = "4,0,2,3"; + } + else { + mnemonic = "MUL"; + format = "4,0,2"; + } + } + if (instr&Sbit) *flagp++='S'; + break; + case 1: + case 3: + /* SWP or MRS/MSR or data processing */ + if ((instr&0x02B00FF0)==0x00000090) { + /* SWP */ + mnemonic = "SWP"; + format = "3,0,[4]"; + if (instr&Bbit) *flagp++='B'; + break; + } + else if ((instr&0x02BF0FFF)==0x000F0000) { + /* MRS */ + mnemonic = "MRS"; + format = (instr&SPSRbit) ? "3,SPSR" : "3,CPSR"; + break; + } + else if ((instr&0x02BFFFF0)==0x0029F000) { + /* MSR psr,Rs */ + mnemonic = "MSR"; + format = (instr&SPSRbit) ? "SPSR,0" : "CPSR,0"; + break; + } + else if ((instr&0x00BFF000)==0x0028F000) { + /* MSR {C,S}PSR_flag,op2 */ + mnemonic = "MSR"; + format = (instr&SPSRbit) ? "SPSR_flg,*" : "CPSR_flg,*"; + if (!(instr&Ibit) && (instr&(15<<4))) +#ifdef INSTR_grok_v4 + goto lMaybeLDRHetc; +#else + goto lUndefined; /* shifted reg in MSR illegal */ +#endif + break; + } + /* fall through here */ +lMaybeLDRHetc: +#ifdef INSTR_grok_v4 + if ((instr&(14<<24))==0 + && ((instr&(9<<4))==(9<<4))) { + /* Might well be LDRH or similar. */ + if ((instr&(Wbit+Pbit))==Wbit) goto lUndefined; /* "class E", case 1 */ + if ((instr&(Lbit+(1<<6)))==(1<<6)) goto lUndefined; /* STRSH etc */ + mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18); + if (instr&(1<<6)) *flagp++='S'; + *flagp++ = (instr&(1<<5)) ? 'B' : 'H'; + format = "3,/"; + /* aargh: */ + if (!(instr&(1<<22))) instr |= Ibit; + is_v4=1; + break; + } +#endif + case 2: + /* data processing */ + { word op21 = instr&(15<<21); + if ((op21==(2<<21) || (op21==(4<<21))) /* ADD or SUB */ + && ((instr&(RNbits+Ibit+Sbit))==RN(15)+Ibit) /* imm, no S */ + /*&& ((instr&(30<<7))==0 || (instr&3))*/) { /* normal rot */ + /* ADD ...,pc,#... or SUB ...,pc,#...: turn into ADR */ + mnemonic = "ADR"; + format = "3,."; + if ((instr&(30<<7))!=0 && !(instr&3)) result.oddbits=1; + break; + } + mnemonic = "AND\0EOR\0SUB\0RSB\0ADD\0ADC\0SBC\0RSC\0" + "TST\0TEQ\0CMP\0CMN\0ORR\0MOV\0BIC\0MVN" /* \0 */ + + (op21 >> 19); + /* Rd needed for all but TST,TEQ,CMP,CMN (8..11) */ + /* Rn needed for all but MOV,MVN (13,15) */ + if (op21 < ( 8<<21)) format = "3,4,*"; + else if (op21 < (12<<21)) { + format = "4,*"; + if (instr&RDbits) { + if ((instr&Sbit) && RD_is(15)) + *flagp++='P'; + else result.oddbits=1; + } + if (!(instr&Sbit)) goto lUndefined; /* CMP etc, no S bit */ + } + else if (op21 & (1<<21)) { + format = "3,*"; + if (instr&RNbits) result.oddbits=1; + } + else format = "3,4,*"; + if (instr&Sbit && (op21<(8<<21) || op21>=(12<<21))) *flagp++='S'; + } + break; + case 4: + case 5: + case 6: + case 7: + /* undefined or STR/LDR */ + if ((instr&Ibit) && (instr&(1<<4))) goto lUndefined; /* "class A" */ + mnemonic = "STR\0LDR" + ((instr&Lbit) >> 18); + format = "3,/"; + if (instr&Bbit) *flagp++='B'; + if ((instr&(Wbit+Pbit))==Wbit) *flagp++='T'; + poss_tt = target_Data; + break; + case 8: + case 9: + /* STM/LDM */ + mnemonic = "STM\0LDM" + ((instr&Lbit) >> 18); + if (RN_is(13)) { + /* r13, so treat as stack */ + word x = (instr&(3<<23)) >> 22; + if (instr&Lbit) x^=6; + { const char * foo = "EDEAFDFA"+x; + *flagp++ = *foo++; + *flagp++ = *foo; + } + } + else { + /* not r13, so don't treat as stack */ + *flagp++ = (instr&Ubit) ? 'I' : 'D'; + *flagp++ = (instr&Pbit) ? 'B' : 'A'; + } + format = "4',%"; + break; + case 10: + case 11: + /* B or BL */ + mnemonic = "B\0BL"+((instr&(1<<24))>>23); + format = "&"; + break; + case 12: + case 13: + /* STC or LDC */ + if (CP_is(1)) { + /* copro 1: FPU. This is STF or LDF. */ + mnemonic = "STF\0LDF" + ((instr&Lbit) >> 18); + format = "8,/"; + *flagp++ = "SDEP"[fpn]; + poss_tt = (eTargetType)(target_FloatS+fpn); + } + else if (CP_is(2)) { + /* copro 2: this is LFM or SFM. */ + mnemonic = "SFM\0LFM" + ((instr&Lbit) >> 18); + if (!fpn) fpn=4; + if (RN_is(13) && BitsDiffer(23,24)) { + if ((instr&255)!=fpn) goto lNonStackLFM; + /* r13 and U!=P, so treat as stack */ + if (BitsDiffer(20,24)) { + /* L != P, so FD */ + *flagp++ = 'F'; *flagp++ = 'D'; + } + else { + /* L == P, so EA */ + *flagp++ = 'E'; *flagp++ = 'A'; + } + format = "8,(,[4]'"; + } + else { +lNonStackLFM: + /* not r13 or U=P or wrong offset, so don't treat as stack */ + format = "8,(,/"; + poss_tt = target_FloatE; + } + } + else { + /* some other copro number: STC or LDC. */ + mnemonic = "STC\0LDC" + ((instr&Lbit) >> 18); + format = ";,\004,/"; + if (instr&(1<<22)) *flagp++ = 'L'; + poss_tt = target_Unknown; + } + break; + case 14: + /* CDP or MRC/MCR */ + if (instr&(1<<4)) { + /* MRC/MCR. */ + if (CP_is(1)) { + /* copro 1: FPU. */ + if ((instr&Lbit) && RD_is(15)) { + /* MCR in FPU with Rd=r15: comparison (ugh) */ + if (!(instr&(1<<23))) goto lUndefined; /* unused operation */ + mnemonic = "CMF\0\0CNF\0\0CMFE\0CNFE" + (5*(instr&(3<<21)) >> 21); + format = "9,+"; + if (instr&((1<<19)+(7<<5))) + result.badbits=1; /* size,rmode reseved */ + } + else { + /* normal FPU MCR/MRC */ + word op20 = instr&(15<<20); + if (op20>=6<<20) goto lUndefined; + mnemonic = "FLT\0FIX\0WFS\0RFS\0WFC\0RFC" + (op20>>18); + if (op20==0) { + /* FLT instruction */ + format = "9,3"; + { char c = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)]; + if (c=='*') goto lUndefined; else *flagp++=c; + } + if (instr&15) result.oddbits=1; /* Fm and const flag unused */ + } + else { + /* not FLT instruction */ + if (instr&((1<<7)+(1<<19))) + result.badbits=1; /* size bits reserved */ + if (op20==1<<20) { + /* FIX instruction */ + format = "3,+"; + if (opts->flags&disopt_FIXS) + *flagp++ = "SDEP"[((instr>>7)&1) + ((instr>>18)&2)]; + *flagp++ = "\0PMZ"[(instr&(3<<5))>>5]; + if (instr&(7<<15)) result.oddbits=1; /* Fn unused */ + if (instr&(1<<3)) result.badbits=1; /* no immediate consts */ + } + else { + /* neither FLT nor FIX */ + format = "3"; + if (instr&(3<<5)) result.badbits=1; /* rmode reserved */ + if (instr&(15+(7<<15))) result.oddbits=1;/* iFm, Fn unused */ + } + } + } + } + else { + /* some other copro number. Not FPU. */ + /* NB that ObjAsm documentation gets MCR and MRC the wrong way round! + */ + mnemonic = "MCR\0MRC"; + mnemonic += (instr&Lbit) >> 18; + format = ";,:,3,\005,\001-"; + } + } + else { + /* CDP. */ + if (CP_is(1)) { + /* copro 1: FPU. */ + mnemonic = /* dyadics: */ + "ADF\0MUF\0SUF\0RSF\0" + "DVF\0RDF\0POW\0RPW\0" + "RMF\0FML\0FDV\0FRD\0" + "POL\0***\0***\0***\0" + /* monadics: */ + "MVF\0MNF\0ABS\0RND\0" + "SQT\0LOG\0LGN\0EXP\0" + "SIN\0COS\0TAN\0ASN\0" + "ACS\0ATN\0URD\0NRM\0" + + ((instr&(15<<20)) >> 18) /* opcode -> bits 5432 */ + + ((instr&(1<<15)) >> 9); /* monadicP -> bit 6 */ + format = (instr&(1<<15)) ? "8,+" : "8,9,+"; + *flagp++ = "SDE*"[((instr>>7)&1) + ((instr>>18)&2)]; + *flagp++ = "\0PMZ"[(instr&(3<<5))>>5]; + /* NB that foregoing relies on this being the last flag! */ + if (*mnemonic=='*' || *flagchars=='*') goto lUndefined; + } + else { + /* some other copro number. Not FPU. */ + mnemonic = "CDP"; + format = ";,),\004,\005,\001-"; + } + } + break; + case 15: + /* SWI */ + mnemonic = "SWI"; + format = "$"; + break; +/* Nasty hack: this is code that won't be reached in the normal + * course of events, and after the last case of the switch is a + * convenient place for it. + */ +lUndefined: + strcpy(result.text, "Undefined instruction"); + result.undefined = 1; + return &result; + } + *flagp=0; + + /* PHASE 2. Produce string. */ + + { char * op = result.text; + + /* 2a. Mnemonic. */ + + op = append(op,mnemonic); + + /* 2b. Condition code. */ + + { word cond = instr>>28; + if (cond!=14) { + const char * ip = "EQNECSCCMIPLVSVCHILSGELTGTLEALNV"+2*cond; + *op++ = *ip++; + *op++ = *ip; + } + } + + /* 2c. Flags. */ + + { const char * ip = flagchars; + while (*ip) *op++ = *ip++; + } + + /* 2d. A tab character. */ + + *op++ = '\t'; + + /* 2e. Other stuff, determined by format string. */ + + { const char * ip = format; + char c; + + const char * * regnames = opts->regnames; + word oflags = opts->flags; + + while ((c=*ip++) != 0) { + switch(c) { + case '$': + result.is_SWI = 1; + result.swinum = instr&0x00FFFFFF; + result.addrstart = op; + op += sprintf(op, "&%lX", result.swinum); + break; + case '%': + *op++='{'; + { word w = instr&0xFFFF; + int i=0; + while (w) { + int j; + while (!(w&(1ul<1) ? '-' : ','; + op = append(op, regnames[j]); + } + i=j; w=(w>>(j+1))<<(j+1); + if (w) *op++=','; + } + } + *op++='}'; + if (instr&(1<<22)) *op++='^'; + break; + case '&': + { address target = (addr+8 + ((((int)instr)<<8)>>6)) & 0x03FFFFFC; + result.addrstart = op; + op = hex8(op, target); + result.target_type = target_Code; + result.target = target; + } + break; + case '\'': +lPling: + if (instr&Wbit) *op++='!'; + break; + case '(': + *op++ = (char)('0'+fpn); + break; + case ')': + { word w = (instr>>20)&15; + if (w>=10) { *op++='1'; *op++=(char)('0'-10+w); } + else *op++=(char)(w+'0'); + } + break; + case '*': + case '.': + if (instr&Ibit) { + /* immediate constant */ + word imm8 = (instr&255); + word rot = (instr>>7)&30; + if (rot && !(imm8&3) && c=='*') { + /* Funny immediate const. Guaranteed not '.', btw */ + *op++='#'; *op++='&'; + *op++="0123456789ABCDEF"[imm8>>4]; + *op++="0123456789ABCDEF"[imm8&15]; + *op++=','; + op = num(op, rot); + } + else { + imm8 = (imm8>>rot) | (imm8<<(32-rot)); + if (c=='*') { + *op++='#'; + if (imm8>256 && ((imm8&(imm8-1))==0)) { + /* only one bit set, and that later than bit 8. + * Represent as 1<<... . + */ + op = append(op,"1<<"); + { int n=0; + while (!(imm8&15)) { n+=4; imm8=imm8>>4; } + /* Now imm8 is 1, 2, 4 or 8. */ + n += (0x30002010 >> 4*(imm8-1))&15; + op = num(op, n); + } + } + else { + if (((int)imm8)<0 && ((int)imm8)>-100) { + *op++='-'; imm8=-imm8; + } + op = num(op, imm8); + } + } + else { + address a = addr+8; + if (instr&(1<<22)) a-=imm8; else a+=imm8; + result.addrstart=op; + op = hex8(op, a); + result.target=a; result.target_type=target_Unknown; + } + } + } + else { + /* rotated register */ + const char * rot = "LSL\0LSR\0ASR\0ROR" + ((instr&(3<<5)) >> 3); + op = append(op, regnames[instr&15]); + if (instr&(1<<4)) { + /* register rotation */ + if (instr&(1<<7)) goto lUndefined; + *op++=','; if (oflags&disopt_CommaSpace) *op++=' '; + op = append(op,rot); *op++=' '; + op = append(op,regnames[(instr&(15<<8))>>8]); + } + else { + /* constant rotation */ + word n = instr&(31<<7); + if (!n) { + if (!(instr&(3<<5))) break; + else if ((instr&(3<<5))==(3<<5)) { + op = append(op, ",RRX"); + break; + } + else n=32<<7; + } + *op++ = ','; if (oflags&disopt_CommaSpace) *op++=' '; + op = num(append(append(op,rot)," #"),n>>7); + } + } + break; + case '+': + if (instr&(1<<3)) { + word w = instr&7; + *op++='#'; + if (w<6) *op++=(char)('0'+w); + else op = append(op, w==6 ? "0.5" : "10"); + } + else { + *op++='f'; + *op++=(char)('0'+(instr&7)); + } + break; + case ',': + *op++=','; + if (oflags&disopt_CommaSpace) *op++=' '; + break; + case '-': + { word w = instr&(7<<5); + if (w) { + *op++=','; + if (oflags&disopt_CommaSpace) *op++=' '; + *op++ = (char)('0'+(w>>5)); + } + } + break; + case '/': + result.addrstart = op; + *op++='['; + op = append(op, regnames[(instr&RNbits)>>16]); + if (!(instr&Pbit)) *op++=']'; + *op++=','; if (oflags&disopt_CommaSpace) *op++=' '; + /* For following, NB that bit 25 is always 0 for LDC, SFM etc */ + if (instr&Ibit) { + /* shifted offset */ + if (!(instr&Ubit)) *op++='-'; + /* We're going to transfer to '*', basically. The stupid + * thing is that the meaning of bit 25 is reversed there; + * I don't know why the designers of the ARM did that. + */ + instr ^= Ibit; + if (instr&(1<<4)) { +#ifdef INSTR_grok_v4 + if (is_v4 && !(instr&(15<<8))) { + ip = (instr&Pbit) ? "0]" : "0"; + break; + } +#else + goto lUndefined; /* LSL r3 forbidden */ +#endif + } + /* Need a ] iff it was pre-indexed; and an optional ! iff + * it's pre-indexed *or* a copro instruction, + * except that FPU operations don't need the !. Bletch. + */ + if (instr&Pbit) ip="*]'"; + else if (instr&(1<<27)) { + if (CP_is(1) || CP_is(2)) { + if (!(instr&Wbit)) goto lUndefined; + ip="*"; + } + else ip="*'"; + } + else ip="*"; + } + else { + /* immediate offset */ + word offset; + if (instr&(1<<27)) { + /* LDF or LFM or similar */ + offset = (instr&255)<<2; + } +#ifdef INSTR_grok_v4 + else if (is_v4) offset = (instr&15) + ((instr&(15<<8))>>4); +#endif + else { + /* LDR or STR */ + offset = instr&0xFFF; + } + *op++='#'; + if (!(instr&Ubit)) { + if (offset) *op++='-'; + else result.oddbits=1; + result.offset = -offset; + } + else result.offset = offset; + op = num(op, offset); + if (RN_is(15) && (instr&Pbit)) { + /* Immediate, pre-indexed and PC-relative. Set target. */ + result.target_type = poss_tt; + result.target = (instr&Ubit) ? addr+8 + offset + : addr+8 - offset; + if (!(instr&Wbit)) { + /* no writeback, either. Use friendly form. */ + op = hex8(result.addrstart, result.target); + break; + } + } + if (instr&Pbit) { *op++=']'; goto lPling; } + else if (instr&(1<<27)) { + if (CP_is(1) || CP_is(2)) { + if (!(instr&Wbit)) goto lUndefined; + } + else goto lPling; + } + } + break; + case '0': case '1': case '2': case '3': case '4': + op = append(op, regnames[(instr>>(4*(c-'0')))&15]); + break; + case '5': case '6': case '7': case '8': case '9': + *op++='f'; + *op++=(char)('0' + ((instr>>(4*(c-'5')))&7)); + break; + case ':': + *op++ = (char)('0' + ((instr>>21)&7)); + break; + case ';': + op = reg(op, 'p', instr>>8); + break; + default: + if (c<=5) + op = reg(op, 'c', instr >> (4*(c-1))); + else *op++ = c; + } + } + *op=0; + } + } + + /* DONE! */ + + return &result; +} + +static const char * reg_names[16] = { + "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7", + "r8", "r9", "r10", "r11", "ip", "sp", "lr", "pc" +}; + +static sDisOptions options = { + disopt_CommaSpace, + reg_names +}; + +void ArmDis(unsigned int addr, unsigned int w, char *output) { + pInstruction instr = instr_disassemble(w, addr, &options); + sprintf(output, "%.6lX %.8lX\t%s", addr, w, instr->text); + if (instr->undefined || instr->badbits || instr->oddbits) { + if (instr->undefined) sprintf(output, " [undefined instr]"); + if (instr->badbits) sprintf(output, " [illegal bits]"); + if (instr->oddbits) sprintf(output, " [unexpected bits]"); + } + // zap tabs + while (*output) { + if (*output == '\t') + *output = ' '; + output++; + } +} \ No newline at end of file diff --git a/ext/disarm.h b/ext/disarm.h new file mode 100644 index 0000000000..124a1a572f --- /dev/null +++ b/ext/disarm.h @@ -0,0 +1,33 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + + +// This stuff only disassembles very old ARM but should be sufficient +// for the basics except for MOVW/MOVT. + + + +// Disarm itself has the license you can see in the cpp file. +// I'm not entirely sure it's 100% gpl compatible but it's nearly +// public domain so meh. + +// The only changes I've done is C++ compat and replaced the main +// program with this function. + +void ArmDis(unsigned int addr, unsigned int w, char *output); From 7b4cfb702c2cbb5c5bc83f26d1d96504599f7f21 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 14:20:06 +0100 Subject: [PATCH 09/49] Get rid of CPU class so that MIPSState can be POD, enabling offsetof --- Core/CPU.cpp | 2 +- Core/CPU.h | 19 ++----------------- Core/MIPS/ARM/ArmJit.cpp | 4 ++-- Core/MIPS/MIPS.h | 2 +- Core/MIPS/MIPSDebugInterface.cpp | 2 +- 5 files changed, 7 insertions(+), 22 deletions(-) diff --git a/Core/CPU.cpp b/Core/CPU.cpp index a0abe65ff6..923971085e 100644 --- a/Core/CPU.cpp +++ b/Core/CPU.cpp @@ -20,6 +20,6 @@ // See the MIPS folder, the real stuff is there. -CPU *currentCPU = 0; +MIPSState *currentCPU = 0; int numCPUs; diff --git a/Core/CPU.h b/Core/CPU.h index 06ce01a3e4..ebc28caffb 100644 --- a/Core/CPU.h +++ b/Core/CPU.h @@ -28,24 +28,9 @@ enum GPR_SIZE_64=1 }; -enum CPUType -{ - CPUTYPE_INVALID=0, - CPUTYPE_ALLEGREX=1, - CPUTYPE_ME=2, -}; - -class CPU -{ -private: - u32 id; -public: - virtual void SingleStep() = 0; - virtual int RunLoopUntil(u64 globalTicks) = 0; -}; - #define MAX_NUM_CPU 2 +class MIPSState; -extern CPU *currentCPU; +extern MIPSState *currentCPU; extern int numCPUs; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index e15a78f3ca..1cf1edbb1e 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -135,8 +135,8 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) b->codeSize = GetCodePtr() - b->normalEntry; #ifdef LOGASM - for (int i = 0; i < b->codeSize; i += 4) { - const u32 *codePtr = (const u32 *)(b->normalEntry + i); + for (int i = 0; i < GetCodePtr() - b->checkedEntry; i += 4) { + const u32 *codePtr = (const u32 *)(b->checkedEntry + i); u32 inst = *codePtr; ArmDis((u32)codePtr, inst, temp); INFO_LOG(DYNA_REC, "A: %s", temp); diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 92c440e606..4f769301fe 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -104,7 +104,7 @@ private: u32 m_z; }; -class MIPSState : public CPU +class MIPSState { public: MIPSState(); diff --git a/Core/MIPS/MIPSDebugInterface.cpp b/Core/MIPS/MIPSDebugInterface.cpp index 6c4d7d3d07..c925aaff51 100644 --- a/Core/MIPS/MIPSDebugInterface.cpp +++ b/Core/MIPS/MIPSDebugInterface.cpp @@ -28,7 +28,7 @@ const char *MIPSDebugInterface::disasm(unsigned int address, unsigned int align) { - CPU *x = currentCPU; + MIPSState *x = currentCPU; currentCPU = cpu; static char mojs[256]; From b78ad83f008bee30562f87296a509e229536d40c Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 14:29:03 +0100 Subject: [PATCH 10/49] R10, not R9, points to the mips state... --- Core/MIPS/ARM/ArmCompBranch.cpp | 2 +- Core/MIPS/ARM/ArmJit.cpp | 5 ++--- Core/MIPS/ARM/ArmRegCache.cpp | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 19a557e4cd..191b5fa5dc 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -369,7 +369,7 @@ void Jit::Comp_JumpReg(u32 op) case 8: //jr break; case 9: //jalr - ADD(R1, R9, MIPS_REG_RA * 4); // compute address of RA in ram + ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram ARMABI_MOVI2R(R2, js.compilerPC + 8); STR(R1, R2); break; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 1cf1edbb1e..08af4719c5 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -143,7 +143,6 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) } #endif - NOP(); AlignCode16(); b->originalSize = numInstructions; return b->normalEntry; @@ -168,11 +167,11 @@ void Jit::Comp_Generic(u32 op) } void Jit::MovFromPC(ARMReg r) { - LDR(r, R9, offsetof(MIPSState, pc)); + LDR(r, R10, offsetof(MIPSState, pc)); } void Jit::MovToPC(ARMReg r) { - STR(R9, r, offsetof(MIPSState, pc)); + STR(R10, r, offsetof(MIPSState, pc)); } void Jit::DoDownCount() diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 49b4f5fd05..84488dea88 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -21,7 +21,7 @@ using namespace ArmGen; -#define CTXREG ((ARMReg)9) +#define CTXREG (R10) ArmRegCache::ArmRegCache(MIPSState *mips) : mips_(mips) { } From 779272e7f553680e8510ff566bbb18eeb51a4a3a Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 16:08:33 +0100 Subject: [PATCH 11/49] Teach disarm about MOVW and MOVT. --- ext/disarm.cpp | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/ext/disarm.cpp b/ext/disarm.cpp index f702ebf943..75b4453644 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -274,6 +274,9 @@ static char * num(char * op, word w) { * : copro opcode in bits 21..23 (for MRC/MCR) * ; copro number in bits 8..11 * + * ADDED BY HRYDGARD: + * ^ 16-bit immediate + * * NB that / takes note of bit 22, too, and does its own ! when * appropriate. * @@ -342,7 +345,18 @@ instr_disassemble(word instr, address addr, pDisOptions opts) { case 1: case 3: /* SWP or MRS/MSR or data processing */ - if ((instr&0x02B00FF0)==0x00000090) { + // hrydgard addition: MOVW/MOVT + if ((instr & 0x0FF00000) == 0x03000000) { + mnemonic = "MOVW"; + format = "3,^"; + break; + } + else if ((instr & 0x0FF00000) == 0x03400000) { + mnemonic = "MOVT"; + format = "3,^"; + break; + } + else if ((instr&0x02B00FF0)==0x00000090) { /* SWP */ mnemonic = "SWP"; format = "3,0,[4]"; @@ -647,6 +661,12 @@ lUndefined: while ((c=*ip++) != 0) { switch(c) { + case '^': // hrydgard addition + { + unsigned short imm16 = ((instr & 0x000F0000) >> 4) | (instr & 0x0FFF); + op += sprintf(op, "%04x", imm16); + } + break; case '$': result.is_SWI = 1; result.swinum = instr&0x00FFFFFF; @@ -917,9 +937,9 @@ void ArmDis(unsigned int addr, unsigned int w, char *output) { pInstruction instr = instr_disassemble(w, addr, &options); sprintf(output, "%.6lX %.8lX\t%s", addr, w, instr->text); if (instr->undefined || instr->badbits || instr->oddbits) { - if (instr->undefined) sprintf(output, " [undefined instr]"); - if (instr->badbits) sprintf(output, " [illegal bits]"); - if (instr->oddbits) sprintf(output, " [unexpected bits]"); + if (instr->undefined) sprintf(output, " [undefined instr %08x]", w); + if (instr->badbits) sprintf(output, " [illegal bits %08x]", w); + if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w); } // zap tabs while (*output) { From 5d6ff0bc2091c2b956aee126645c1c3e0abe97d2 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 16:47:44 +0100 Subject: [PATCH 12/49] More disassembly improvements --- ext/disarm.cpp | 19 ++++++++++++++++--- ext/disarm.h | 1 + 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/ext/disarm.cpp b/ext/disarm.cpp index 75b4453644..b75dd8bb4f 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -343,6 +343,15 @@ instr_disassemble(word instr, address addr, pDisOptions opts) { if (instr&Sbit) *flagp++='S'; break; case 1: + if ((instr & 0x0FFFFFF0) == ((18 << 20) | (0xFFF << 8) | (1 << 4))) { + mnemonic = "B"; + format = "0"; + break; + } else if ((instr & 0x0FFFFFF0) == 0x12FFF30) { + mnemonic = "BL"; + format = "0"; + break; + } case 3: /* SWP or MRS/MSR or data processing */ // hrydgard addition: MOVW/MOVT @@ -671,7 +680,7 @@ lUndefined: result.is_SWI = 1; result.swinum = instr&0x00FFFFFF; result.addrstart = op; - op += sprintf(op, "&%lX", result.swinum); + op += sprintf(op, "&%X", result.swinum); break; case '%': *op++='{'; @@ -933,13 +942,17 @@ static sDisOptions options = { reg_names }; +const char *ArmRegName(int r) { + return reg_names[r]; +} void ArmDis(unsigned int addr, unsigned int w, char *output) { pInstruction instr = instr_disassemble(w, addr, &options); - sprintf(output, "%.6lX %.8lX\t%s", addr, w, instr->text); + sprintf(output, "%08x\t%s", w, instr->text); if (instr->undefined || instr->badbits || instr->oddbits) { if (instr->undefined) sprintf(output, " [undefined instr %08x]", w); if (instr->badbits) sprintf(output, " [illegal bits %08x]", w); - if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w); + strcat(output, " ? (extra bits)"); + //if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w); } // zap tabs while (*output) { diff --git a/ext/disarm.h b/ext/disarm.h index 124a1a572f..5032880a18 100644 --- a/ext/disarm.h +++ b/ext/disarm.h @@ -30,4 +30,5 @@ // The only changes I've done is C++ compat and replaced the main // program with this function. +const char *ArmRegName(int r); void ArmDis(unsigned int addr, unsigned int w, char *output); From 8c06edc47b52b211f2eac63bd67de019c50b7a99 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 17:03:17 +0100 Subject: [PATCH 13/49] It's getting close to the first totally unoptimized jit run. --- Common/ArmABI.cpp | 10 +++++ Common/ArmEmitter.h | 1 + Core/HLE/sceKernelThread.cpp | 8 +++- Core/MIPS/ARM/ArmCompALU.cpp | 5 ++- Core/MIPS/ARM/ArmCompBranch.cpp | 11 +++--- Core/MIPS/ARM/ArmJit.cpp | 37 +++++++++++++++---- Core/MIPS/ARM/ArmRegCache.cpp | 15 +++++--- Core/MIPS/ARM/ArmRegCache.h | 6 +-- Core/MIPS/x86/CompBranch.cpp | 2 - Core/MemMapFunctions.cpp | 2 +- Windows/main.cpp | 4 ++ .../src/org/ppsspp/ppsspp/PpssppActivity.java | 2 +- ext/disarm.cpp | 6 ++- 13 files changed, 78 insertions(+), 31 deletions(-) diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index b23f72aeab..7aa1a315ab 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -36,6 +36,16 @@ void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) BL(R14); POP(5, R0, R1, R2, R3, _LR); } + +void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg) +{ + ARMABI_MOVI2R(R14, Mem(func)); + PUSH(1, _LR); + ARMABI_MOVI2R(R0, Arg); + BL(R14); + POP(1, _LR); +} + void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) { ARMABI_MOVI2R(R14, Mem(func)); diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index a55671ffa2..f3b1b4ad17 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -467,6 +467,7 @@ public: // where appropriate. void ARMABI_CallFunction(void *func); void ARMABI_CallFunctionC(void *func, u32 Arg0); + void ARMABI_CallFunctionCNoSave(void *func, u32 Arg0); void ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2); void ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3); void ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); diff --git a/Core/HLE/sceKernelThread.cpp b/Core/HLE/sceKernelThread.cpp index e872e20764..df4945d9bb 100644 --- a/Core/HLE/sceKernelThread.cpp +++ b/Core/HLE/sceKernelThread.cpp @@ -1282,7 +1282,11 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 t->nt.gpreg = 0; // sceKernelStartThread will take care of this. t->moduleId = moduleId; - strncpy(t->nt.name, name, 32); + if (name) { + strncpy(t->nt.name, name, 32); + } else { + ERROR_LOG(HLE, "Threads must have names!"); + } return t; } @@ -1994,7 +1998,7 @@ ActionAfterMipsCall *Thread::getRunningCallbackAction() if (this->GetUID() == currentThread && g_inCbCount > 0) { MipsCall *call = mipsCalls.get(this->currentCallbackId); - ActionAfterMipsCall *action; + ActionAfterMipsCall *action = 0; if (call) action = dynamic_cast(call->doAfter); diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index d467f01d1c..f9de2ef4ab 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -59,6 +59,7 @@ namespace MIPSComp switch (op >> 26) { + /* case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { @@ -77,7 +78,7 @@ namespace MIPSComp gpr.ReleaseSpillLocks(); } break; - } + }*/ /* case 13: // OR { @@ -123,11 +124,11 @@ namespace MIPSComp gpr.UnlockAll(); break; - */ case 15: //R(rt) = uimm << 16; break; //lui gpr.SetImm(rt, uimm << 16); break; + */ default: Comp_Generic(op); diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 191b5fa5dc..21059fc28b 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -55,7 +55,7 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) bool delaySlotIsNice = GetOutReg(delaySlotOp) != rt && GetOutReg(delaySlotOp) != rs;// IsDelaySlotNice(op, delaySlotOp); if (!delaySlotIsNice) { - ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC); + //ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC); } // The delay slot being nice doesn't really matter though... if (rt == 0) @@ -63,11 +63,12 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) gpr.MapReg(rs, MAP_INITVAL); CMP(gpr.R(rs), Operand2(0)); } + /* else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' { gpr.MapReg(rt, MAP_INITVAL); CMP(gpr.R(rt), Operand2(0)); - } + }*/ else { gpr.SpillLock(rs, rt); @@ -208,7 +209,7 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp); if (!delaySlotIsNice) { - ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :("); + //ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :("); } FlushAll(); @@ -270,7 +271,7 @@ void Jit::BranchVFPUFlag(u32 op, ArmGen::CCFlags cc, bool likely) bool delaySlotIsNice = IsDelaySlotNice(op, delaySlotOp); if (!delaySlotIsNice) { - ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :("); + //ERROR_LOG(CPU, "Not nice delay slot in BranchFPFlag :("); } FlushAll(); @@ -337,7 +338,7 @@ void Jit::Comp_Jump(u32 op) case 3: //jal ARMABI_MOVI2R(R0, Operand2(js.compilerPC + 8, TYPE_IMM)); - ARMABI_MOVI2R(R1, Operand2((u32)&mips_->r[MIPS_REG_RA], TYPE_IMM)); + ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram STR(R1, R0); WriteExit(targetAddr, 0); break; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 08af4719c5..e3c3eec64e 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -83,6 +83,30 @@ void Hullo(int a, int b, int c, int d) { INFO_LOG(DYNA_REC, "Hullo %08x %08x %08x %08x", a, b, c, d); } +static void DisassembleArm(const u8 *data, int size) { + char temp[256]; + for (int i = 0; i < size; i += 4) { + const u32 *codePtr = (const u32 *)(data + i); + u32 inst = codePtr[0]; + u32 next = (i < size - 4) ? codePtr[1] : 0; + // MAGIC SPECIAL CASE for MOVW/MOVT readability! + if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) { + u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF); + u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); + int reg0 = (inst & 0x0000F000) >> 12; + int reg1 = (next & 0x0000F000) >> 12; + if (reg0 == reg1) { + sprintf(temp, "%08x MOV32? %s, %04x%04x", (u32)inst, ArmRegName(reg0), hi, low); + INFO_LOG(DYNA_REC, "A: %s", temp); + i += 4; + continue; + } + } + ArmDis((u32)codePtr, inst, temp); + INFO_LOG(DYNA_REC, "A: %s", temp); + } +} + const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { js.cancel = false; @@ -130,17 +154,13 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) } #ifdef LOGASM MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); - INFO_LOG(DYNA_REC, "M: %s", temp); + INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); #endif b->codeSize = GetCodePtr() - b->normalEntry; + #ifdef LOGASM - for (int i = 0; i < GetCodePtr() - b->checkedEntry; i += 4) { - const u32 *codePtr = (const u32 *)(b->checkedEntry + i); - u32 inst = *codePtr; - ArmDis((u32)codePtr, inst, temp); - INFO_LOG(DYNA_REC, "A: %s", temp); - } + DisassembleArm(b->checkedEntry, GetCodePtr() - b->checkedEntry); #endif AlignCode16(); @@ -228,7 +248,8 @@ void Jit::WriteExit(u32 destination, int exit_num) void Jit::WriteSyscallExit() { DoDownCount(); - B((const void *)asm_.dispatcherCheckCoreState); + ARMABI_MOVI2R(R0, (u32)asm_.dispatcherCheckCoreState); + B(R0); } diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 84488dea88..538e269cc2 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -37,8 +37,10 @@ void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { ar[i].allocLock = false; ar[i].isDirty = false; } - for (int i = 0; i < 32; i++) { + for (int i = 0; i < NUM_MIPSREG; i++) { mr[i].loc = ML_MEM; + mr[i].reg = INVALID_REG; + mr[i].imm = -1; } } @@ -109,12 +111,15 @@ void ArmRegCache::FlushArmReg(ARMReg r) { // Nothing to do return; } - if (ar[r].isDirty) { - if (mr[ar[r].mipsReg].loc == ML_MEM) + if (ar[r].mipsReg != -1) { + if (ar[r].isDirty && mr[ar[r].mipsReg].loc == ML_ARMREG) emit->STR(CTXREG, r, 4 * ar[r].mipsReg); - ar[r].isDirty = false; - ar[r].mipsReg = -1; + mr[ar[r].mipsReg].loc = ML_MEM; + } else { + ERROR_LOG(HLE, "Dirty but no mipsreg?"); } + ar[r].isDirty = false; + ar[r].mipsReg = -1; } void ArmRegCache::FlushMipsReg(MIPSReg r) { diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index dfbcf2b4da..73e6c50642 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -24,10 +24,10 @@ using namespace ArmGen; -// R2 to R9: mapped MIPS regs +// R2 to R8: mapped MIPS regs +// R9 = code pointers +// R10 = MIPS context // R11 = base pointer -// R12 = MIPS context -// R14 = code pointers // Special MIPS registers: enum { diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index a3cd04f889..4b95857b60 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -447,8 +447,6 @@ void Jit::Comp_JumpReg(u32 op) void Jit::Comp_Syscall(u32 op) { - // This will most often be called from Comp_JumpReg (jr ra) so we take over the exit sequence... - FlushAll(); ABI_CallFunctionC((void *)(&CallSyscall), op); diff --git a/Core/MemMapFunctions.cpp b/Core/MemMapFunctions.cpp index ec24911f76..2e0f2a5c15 100644 --- a/Core/MemMapFunctions.cpp +++ b/Core/MemMapFunctions.cpp @@ -55,7 +55,7 @@ u8 *GetPointer(const u32 address) } else { - ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); + ERROR_LOG(MEMMAP, "Unknown GetPointer %08x PC %08x LR %08x", address, currentMIPS->pc, currentMIPS->r[MIPS_REG_RA]); return 0; } } diff --git a/Windows/main.cpp b/Windows/main.cpp index 8bc5bb5260..2b5ddc1db9 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -23,6 +23,7 @@ #include "../Core/Config.h" #include "../Core/SaveState.h" #include "EmuThread.h" +#include "ext/disarm.h" #include "LogManager.h" #include "ConsoleListener.h" @@ -50,6 +51,9 @@ CMemoryDlg *memoryWindow[MAX_CPUCOUNT]; int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { + char temp[256]; + ArmDis(0, 0xE12fff10, temp); + Common::EnableCrashingOnCrashes(); const char *fileToStart = NULL; diff --git a/android/src/org/ppsspp/ppsspp/PpssppActivity.java b/android/src/org/ppsspp/ppsspp/PpssppActivity.java index 3c990c7aa9..c752713aaa 100644 --- a/android/src/org/ppsspp/ppsspp/PpssppActivity.java +++ b/android/src/org/ppsspp/ppsspp/PpssppActivity.java @@ -15,4 +15,4 @@ public class PpssppActivity extends NativeActivity { { return false; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/ext/disarm.cpp b/ext/disarm.cpp index b75dd8bb4f..f2f7ffc193 100644 --- a/ext/disarm.cpp +++ b/ext/disarm.cpp @@ -951,8 +951,10 @@ void ArmDis(unsigned int addr, unsigned int w, char *output) { if (instr->undefined || instr->badbits || instr->oddbits) { if (instr->undefined) sprintf(output, " [undefined instr %08x]", w); if (instr->badbits) sprintf(output, " [illegal bits %08x]", w); - strcat(output, " ? (extra bits)"); - //if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w); + + // HUH? LDR and STR gets this a lot + // strcat(output, " ? (extra bits)"); + // if (instr->oddbits) sprintf(output, " [unexpected bits %08x]", w); } // zap tabs while (*output) { From e3a4ed510c0fa6d8cf7a5f4373cb2226a3e419a8 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 19:30:28 +0100 Subject: [PATCH 14/49] Fix bug in x86 jit :) --- Core/MIPS/x86/CompBranch.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 4b95857b60..64a85d0740 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -80,9 +80,10 @@ void Jit::BranchRSRTComp(u32 op, Gen::CCFlags cc, bool likely) } delaySlotIsNice = false; // Until we have time to fully fix this - if (rs == 0) + if (rt == 0) { - CMP(32, gpr.R(rt), Imm32(0)); + gpr.KillImmediate(rs, true, true); + CMP(32, gpr.R(rs), Imm32(0)); } else { @@ -448,7 +449,8 @@ void Jit::Comp_JumpReg(u32 op) void Jit::Comp_Syscall(u32 op) { FlushAll(); - ABI_CallFunctionC((void *)(&CallSyscall), op); + + ABI_CallFunctionC((void *)&CallSyscall, op); WriteSyscallExit(); js.compiling = false; From 8915677241ef533fc97bb05a067d32b26abc0849 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 21:24:42 +0100 Subject: [PATCH 15/49] More progress but it weirds out... --- Common/ArmABI.cpp | 10 ++-- Common/LogManager.cpp | 2 + Common/LogManager.h | 1 + Core/Core.cpp | 2 + Core/CoreTiming.cpp | 1 + Core/HLE/sceDisplay.cpp | 1 + Core/MIPS/ARM/ArmAsm.cpp | 41 ++++++++------- Core/MIPS/ARM/ArmCompBranch.cpp | 88 +++++++++++++++++++++++---------- Core/MIPS/ARM/ArmJit.cpp | 83 +++++++++++++++---------------- Core/MIPS/ARM/ArmJitCache.cpp | 2 +- Core/MIPS/MIPSInt.cpp | 1 + Core/MIPS/x86/Asm.cpp | 4 +- Core/MIPS/x86/Jit.cpp | 21 ++++---- android/jni/Android.mk | 4 +- 14 files changed, 148 insertions(+), 113 deletions(-) diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index 7aa1a315ab..82dbd7b762 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -23,15 +23,15 @@ using namespace ArmGen; // If passing arguments, don't use this. void ARMXEmitter::ARMABI_CallFunction(void *func) { - ARMABI_MOVI2R(R14, Mem(func)); PUSH(5, R0, R1, R2, R3, _LR); + ARMABI_MOVI2R(R14, Mem(func)); BL(R14); POP(5, R0, R1, R2, R3, _LR); } void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) { - ARMABI_MOVI2R(R14, Mem(func)); PUSH(5, R0, R1, R2, R3, _LR); + ARMABI_MOVI2R(R14, Mem(func)); ARMABI_MOVI2R(R0, Arg); BL(R14); POP(5, R0, R1, R2, R3, _LR); @@ -39,8 +39,8 @@ void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg) { - ARMABI_MOVI2R(R14, Mem(func)); PUSH(1, _LR); + ARMABI_MOVI2R(R14, Mem(func)); ARMABI_MOVI2R(R0, Arg); BL(R14); POP(1, _LR); @@ -48,8 +48,8 @@ void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg) void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) { - ARMABI_MOVI2R(R14, Mem(func)); PUSH(5, R0, R1, R2, R3, _LR); + ARMABI_MOVI2R(R14, Mem(func)); ARMABI_MOVI2R(R0, Arg1); ARMABI_MOVI2R(R1, Arg2); BL(R14); @@ -58,8 +58,8 @@ void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) } void ARMXEmitter::ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3) { - ARMABI_MOVI2R(R14, Mem(func)); PUSH(5, R0, R1, R2, R3, _LR); + ARMABI_MOVI2R(R14, Mem(func)); ARMABI_MOVI2R(R0, Arg1); ARMABI_MOVI2R(R1, Arg2); ARMABI_MOVI2R(R2, Arg3); diff --git a/Common/LogManager.cpp b/Common/LogManager.cpp index f6fd94c98f..64b59e11f9 100644 --- a/Common/LogManager.cpp +++ b/Common/LogManager.cpp @@ -149,6 +149,8 @@ void LogManager::LoadConfig(IniFile::Section *section) void LogManager::Log(LogTypes::LOG_LEVELS level, LogTypes::LOG_TYPE type, const char *file, int line, const char *format, va_list args) { + std::lock_guard lk(m_log_lock); + char temp[MAX_MSGLEN]; char msg[MAX_MSGLEN * 2]; LogContainer *log = m_Log[type]; diff --git a/Common/LogManager.h b/Common/LogManager.h index faef623276..846fdf2658 100644 --- a/Common/LogManager.h +++ b/Common/LogManager.h @@ -106,6 +106,7 @@ private: ConsoleListener *m_consoleLog; DebuggerLogListener *m_debuggerLog; static LogManager *m_logManager; // Singleton. Ugh. + std::mutex m_log_lock; LogManager(); ~LogManager(); diff --git a/Core/Core.cpp b/Core/Core.cpp index 49f7846bb4..4b7e25f41e 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -65,6 +65,7 @@ bool Core_IsStepping() void Core_RunLoop() { currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); + INFO_LOG(DYNA_REC, "Wooo"); } void Core_DoSingleStep() @@ -93,6 +94,7 @@ reswitch: case CORE_RUNNING: //1: enter a fast runloop Core_RunLoop(); + INFO_LOG(DYNA_REC, "Yay"); break; // We should never get here on Android. diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 70a9431f5c..0fbad2a7fb 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -462,6 +462,7 @@ void MoveEvents() void Advance() { + WARN_LOG(HLE, "ADVANCE!"); int cyclesExecuted = slicelength - downcount; globalTimer += cyclesExecuted; downcount = slicelength; diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index e85be05e2c..1c9c085a99 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -269,6 +269,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { // Tell the emu core that it's time to stop emulating // Win32 doesn't need this. #ifndef _WIN32 + ERROR_LOG(HLE, "End oph phrame"); coreState = CORE_NEXTFRAME; #endif } diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 9df0a58f37..2e7691ba35 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -62,20 +62,19 @@ extern volatile CoreState coreState; void Jit() { - INFO_LOG(HLE, "Compiling at %08x", currentMIPS->pc); MIPSComp::jit->Compile(currentMIPS->pc); } -void ImHere() { - static int i = 0; - i++; - INFO_LOG(HLE, "I'm too here %i", i); -} -void ImHere2(u32 hej, u32 hej2) { - static int i = 0; - i++; - INFO_LOG(HLE, "I'm here2 %i %08x %08x", i, hej, hej2); +void ShowPC() { + if (currentMIPS) { + WARN_LOG(HLE, "PC : %08x", currentMIPS->pc); + } else { + ERROR_LOG(HLE, "Universe corrupt?"); + } } + +void DisassembleArm(const u8 *data, int size); + // PLAN: no more block numbers - crazy opcodes just contain offset within // dynarec buffer // At this offset - 4, there is an int specifying the block number. @@ -88,7 +87,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) SetCC(CC_AL); - PUSH(8, R5, R6, R7, R8, R9, R10, R11, _LR); + PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, _LR); // Fixed registers, these are always kept when in Jit context. // R13 cannot be used as it's the stack pointer. @@ -96,9 +95,6 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) ARMABI_MOVI2R(R10, (u32)mips); ARMABI_MOVI2R(R9, (u32)jit->GetBlockCache()->GetCodePointers()); - // PROVEN: We Get Here - ARMABI_CallFunction((void *)&ImHere); - outerLoop = GetCodePtr(); ARMABI_CallFunction((void *)&CoreTiming::Advance); FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time @@ -114,18 +110,19 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // 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(CC_LT); + FixupBranch bail = B_CC(CC_MI); SetJumpTarget(skipToRealDispatch); dispatcherNoCheck = GetCodePtr(); // Debug + // ARMABI_CallFunction((void *)&ShowPC); - ARMABI_MOVI2R(R0, (u32)&mips->pc); - LDR(R0, R0); + LDR(R0, R10, offsetof(MIPSState, pc)); ARMABI_MOVI2R(R1, Memory::MEMVIEW32_MASK); // can be done with single MOVN instruction AND(R0, R0, R1); @@ -134,7 +131,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); - FixupBranch notfound = B_CC(CC_NEQ); + FixupBranch notfound = B_CC(CC_NEQ); // TODO : No need for a branch really, can use CCs. // IDEA - we have 24 bits, why not just use offsets from base of code? if (enableDebug) { @@ -162,9 +159,11 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) SetJumpTarget(badCoreState); - ARMABI_CallFunction((void *)&ImHere); + breakpointBailout = GetCodePtr(); - //Landing pad for drec space + POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns - POP(8, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns + INFO_LOG(HLE, "THE DISASM ========================"); + DisassembleArm(enterCode, GetCodePtr() - enterCode); + INFO_LOG(HLE, "END OF THE DISASM ========================"); } \ No newline at end of file diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 21059fc28b..75a1739ff7 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -44,6 +44,10 @@ namespace MIPSComp void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op&0xFFFF)<<2; int rt = _RT; int rs = _RS; @@ -52,24 +56,25 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC+4); //Compile the delay slot - bool delaySlotIsNice = GetOutReg(delaySlotOp) != rt && GetOutReg(delaySlotOp) != rs;// IsDelaySlotNice(op, delaySlotOp); + bool delaySlotIsNice = GetOutReg(delaySlotOp) != rt && GetOutReg(delaySlotOp) != rs; if (!delaySlotIsNice) { //ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC); } // The delay slot being nice doesn't really matter though... + /* if (rt == 0) { gpr.MapReg(rs, MAP_INITVAL); CMP(gpr.R(rs), Operand2(0)); - } + }*/ /* else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' { gpr.MapReg(rt, MAP_INITVAL); CMP(gpr.R(rt), Operand2(0)); - }*/ - else + } + else*/ { gpr.SpillLock(rs, rt); gpr.MapReg(rs, MAP_INITVAL); @@ -86,10 +91,11 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) // preserve flag around the delay slot! Maybe this is not always necessary on ARM where // we can (mostly) control whether we set the flag or not. Of course, if someone puts an slt in to the // delay slot, we're screwed. - MRS(R8); // Save flags register. R4 is preserved through function calls and is not allocated. + MRS(R8); // Save flags register. R8 is preserved through function calls and is not allocated. CompileAt(js.compilerPC + 4); FlushAll(); _MSR(true, false, R8); // Restore flags register + ptr = B_CC(cc); } else @@ -113,6 +119,10 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op&0xFFFF)<<2; int rs = _RS; u32 targetAddr = js.compilerPC + offset + 4; @@ -128,8 +138,8 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) CMP(gpr.R(rs), Operand2(0)); FlushAll(); - js.inDelaySlot = true; ArmGen::FixupBranch ptr; + js.inDelaySlot = true; if (!likely) { // preserve flag around the delay slot! Maybe this is not always necessary on ARM where @@ -201,6 +211,10 @@ void Jit::Comp_RelBranchRI(u32 op) // If likely is set, discard the branch slot if NOT taken. void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) { + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int offset = (signed short)(op & 0xFFFF) << 2; u32 targetAddr = js.compilerPC + offset + 4; @@ -322,11 +336,19 @@ void Jit::Comp_VBranch(u32 op) js.compiling = false; } +void PrintAtExit() { + INFO_LOG(HLE, "at jump"); +} + void Jit::Comp_Jump(u32 op) { - u32 off = ((op & 0x3FFFFFF) << 2); + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } + u32 off = ((op & 0x03FFFFFF) << 2); u32 targetAddr = (js.compilerPC & 0xF0000000) | off; - //Delay slot + // Delay slot CompileAt(js.compilerPC + 4); FlushAll(); @@ -337,8 +359,8 @@ void Jit::Comp_Jump(u32 op) break; case 3: //jal - ARMABI_MOVI2R(R0, Operand2(js.compilerPC + 8, TYPE_IMM)); ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram + ARMABI_MOVI2R(R0, js.compilerPC + 8); STR(R1, R0); WriteExit(targetAddr, 0); break; @@ -352,34 +374,51 @@ void Jit::Comp_Jump(u32 op) void Jit::Comp_JumpReg(u32 op) { - u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); - bool delaySlotIsNice = GetOutReg(delaySlotOp) != _RS; - // Do what with that information? + if (js.inDelaySlot) { + ERROR_LOG(JIT, "Branch in delay slot at %08x", js.compilerPC); + return; + } int rs = _RS; + u32 delaySlotOp = Memory::ReadUnchecked_U32(js.compilerPC + 4); + bool delaySlotIsNice = GetOutReg(delaySlotOp) != rs; + // Do what with that information? + delaySlotIsNice = false; + gpr.MapReg(rs, MAP_INITVAL); - // Delay slot - MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid - CompileAt(js.compilerPC + 4); - FlushAll(); - MOV(R0, R8); // TODO: Remove. - + if (delaySlotIsNice) { + CompileAt(js.compilerPC + 4); + MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented + FlushAll(); + MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking. + } else { + // Delay slot + MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented + MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking. + CompileAt(js.compilerPC + 4); + FlushAll(); + if (!js.compiling) { + // INFO_LOG(HLE, "Syscall in delay slot!"); + return; + } + } + switch (op & 0x3f) { case 8: //jr break; case 9: //jalr ADD(R1, R10, MIPS_REG_RA * 4); // compute address of RA in ram - ARMABI_MOVI2R(R2, js.compilerPC + 8); - STR(R1, R2); + ARMABI_MOVI2R(R0, js.compilerPC + 8); + STR(R1, R0); break; default: _dbg_assert_msg_(CPU,0,"Trying to compile instruction that can't be compiled"); break; } - WriteExitDestInR(R0); + WriteExitDestInR(R8); js.compiling = false; } @@ -388,15 +427,10 @@ void Jit::Comp_Syscall(u32 op) { FlushAll(); - // By putting mips_ in a register and using offsets, we could get rid of one of the constant-sets. - ARMABI_MOVI2R(R0, (u32)&mips_->r[MIPS_REG_RA]); - LDR(R0, R0); - ARMABI_MOVI2R(R1, (u32)&mips_->pc); - STR(R1, R0); - ARMABI_CallFunctionC((void *)&CallSyscall, op); WriteSyscallExit(); + js.compiling = false; } } // namespace Mipscomp diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index e3c3eec64e..1b17ef6d55 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -27,6 +27,30 @@ #include "../../ext/disarm.h" +void DisassembleArm(const u8 *data, int size) { + char temp[256]; + for (int i = 0; i < size; i += 4) { + const u32 *codePtr = (const u32 *)(data + i); + u32 inst = codePtr[0]; + u32 next = (i < size - 4) ? codePtr[1] : 0; + // MAGIC SPECIAL CASE for MOVW/MOVT readability! + if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) { + u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF); + u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); + int reg0 = (inst & 0x0000F000) >> 12; + int reg1 = (next & 0x0000F000) >> 12; + if (reg0 == reg1) { + sprintf(temp, "%08x MOV32? %s, %04x%04x", (u32)inst, ArmRegName(reg0), hi, low); + INFO_LOG(DYNA_REC, "A: %s", temp); + i += 4; + continue; + } + } + ArmDis((u32)codePtr, inst, temp); + INFO_LOG(DYNA_REC, "A: %s", temp); + } +} + namespace MIPSComp { @@ -62,6 +86,7 @@ void Jit::CompileAt(u32 addr) void Jit::Compile(u32 em_address) { + //ERROR_LOG(CPU, "Compile %08x", em_address); if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) { ClearCache(); @@ -76,37 +101,14 @@ void Jit::RunLoopUntil(u64 globalticks) { // TODO: copy globalticks somewhere ((void (*)())asm_.enterCode)(); - INFO_LOG(DYNA_REC, "Left asm code"); + INFO_LOG(DYNA_REC, "Left asm code like a boss!"); + INFO_LOG(DYNA_REC, "or Two!"); } void Hullo(int a, int b, int c, int d) { INFO_LOG(DYNA_REC, "Hullo %08x %08x %08x %08x", a, b, c, d); } -static void DisassembleArm(const u8 *data, int size) { - char temp[256]; - for (int i = 0; i < size; i += 4) { - const u32 *codePtr = (const u32 *)(data + i); - u32 inst = codePtr[0]; - u32 next = (i < size - 4) ? codePtr[1] : 0; - // MAGIC SPECIAL CASE for MOVW/MOVT readability! - if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) { - u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF); - u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); - int reg0 = (inst & 0x0000F000) >> 12; - int reg1 = (next & 0x0000F000) >> 12; - if (reg0 == reg1) { - sprintf(temp, "%08x MOV32? %s, %04x%04x", (u32)inst, ArmRegName(reg0), hi, low); - INFO_LOG(DYNA_REC, "A: %s", temp); - i += 4; - continue; - } - } - ArmDis((u32)codePtr, inst, temp); - INFO_LOG(DYNA_REC, "A: %s", temp); - } -} - const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { js.cancel = false; @@ -122,7 +124,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) SetCC(CC_LT); ARMABI_MOVI2R(R0, js.blockStart); MovToPC(R0); - ARMABI_MOVI2R(R0, (u32)asm_.outerLoop); + ARMABI_MOVI2R(R0, (u32)asm_.outerLoop); // downcount hit zero - go advance. B(R0); SetCC(CC_AL); @@ -134,7 +136,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) int numInstructions = 0; int cycles = 0; -#define LOGASM +// #define LOGASM #ifdef LOGASM char temp[256]; #endif @@ -162,7 +164,6 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) #ifdef LOGASM DisassembleArm(b->checkedEntry, GetCodePtr() - b->checkedEntry); #endif - AlignCode16(); b->originalSize = numInstructions; return b->normalEntry; @@ -196,7 +197,7 @@ void Jit::MovToPC(ARMReg r) { void Jit::DoDownCount() { - ARMABI_MOVI2R(R0, Mem(&CoreTiming::downcount)); + ARMABI_MOVI2R(R0, (u32)&CoreTiming::downcount); LDR(R1, R0); if(js.downcountAmount < 255) // We can enlarge this if we used rotations { @@ -211,14 +212,6 @@ void Jit::DoDownCount() } } -void Jit::WriteExitDestInR(ARMReg Reg) -{ - MovToPC(Reg); - DoDownCount(); - // TODO: shouldn't need an indirect branch here... - ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); - B(R0); -} void Jit::WriteExit(u32 destination, int exit_num) { @@ -230,14 +223,11 @@ void Jit::WriteExit(u32 destination, int exit_num) // Link opportunity! int block = blocks.GetBlockNumberFromStartAddress(destination); - if (block >= 0 && jo.enableBlocklink) - { + if (block >= 0 && jo.enableBlocklink) { // It exists! Joy of joy! B(blocks.GetBlock(block)->checkedEntry); b->linkStatus[exit_num] = true; - } - else - { + } else { ARMABI_MOVI2R(R0, destination); MovToPC(R0); ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); @@ -245,6 +235,15 @@ void Jit::WriteExit(u32 destination, int exit_num) } } +void Jit::WriteExitDestInR(ARMReg Reg) +{ + MovToPC(Reg); + DoDownCount(); + // TODO: shouldn't need an indirect branch here... + ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); + B(R0); +} + void Jit::WriteSyscallExit() { DoDownCount(); diff --git a/Core/MIPS/ARM/ArmJitCache.cpp b/Core/MIPS/ARM/ArmJitCache.cpp index 7c7b2c76f5..ae08f2a37d 100644 --- a/Core/MIPS/ARM/ArmJitCache.cpp +++ b/Core/MIPS/ARM/ArmJitCache.cpp @@ -107,7 +107,7 @@ void ArmJitBlockCache::Clear() links_to.clear(); block_map.clear(); num_blocks = 0; - memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS); + memset(blockCodePointers, 0xCC, sizeof(u8*)*MAX_NUM_BLOCKS); } void ArmJitBlockCache::ClearSafe() diff --git a/Core/MIPS/MIPSInt.cpp b/Core/MIPS/MIPSInt.cpp index c3bcfe4ab9..13d0d90d5d 100644 --- a/Core/MIPS/MIPSInt.cpp +++ b/Core/MIPS/MIPSInt.cpp @@ -272,6 +272,7 @@ namespace MIPSInt // There's one of these in Star Soldier at 0881808c, which seems benign - it should probably be ignored. if (op == 0x03e00008) return; + ERROR_LOG(HLE, "Jump in delay slot :("); _dbg_assert_msg_(CPU,0,"Jump in delay slot :("); } diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index c0c0b9f64f..5d3bc6ff28 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -87,7 +87,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) dispatcher = GetCodePtr(); // The result of slice decrementation should be in flags if somebody jumped here // IMPORTANT - We jump on negative, not carry!!! - FixupBranch bail = J_CC(CC_BE, true); + FixupBranch bail = J_CC(CC_S, true); SetJumpTarget(skipToRealDispatch); @@ -138,12 +138,10 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) J_CC(CC_Z, outerLoop, true); SetJumpTarget(badCoreState); - //Landing pad for drec space ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); breakpointBailout = GetCodePtr(); - //Landing pad for drec space ABI_PopAllCalleeSavedRegsAndAdjustStack(); RET(); } \ No newline at end of file diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index cfdc23963d..fa7052093d 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -57,6 +57,7 @@ void Jit::CompileAt(u32 addr) void Jit::Compile(u32 em_address) { + ERROR_LOG(CPU, "Compile %08x", em_address); if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) { ClearCache(); @@ -146,19 +147,15 @@ void Jit::WriteExit(u32 destination, int exit_num) // Link opportunity! int block = blocks.GetBlockNumberFromStartAddress(destination); - if (jo.enableBlocklink) - { - if (block >= 0 && jo.enableBlocklink) - { - // It exists! Joy of joy! - JMP(blocks.GetBlock(block)->checkedEntry, true); - b->linkStatus[exit_num] = true; - return; - } + if (block >= 0 && jo.enableBlocklink) { + // It exists! Joy of joy! + JMP(blocks.GetBlock(block)->checkedEntry, true); + b->linkStatus[exit_num] = true; + } else { + // No blocklinking. + MOV(32, M(&mips_->pc), Imm32(destination)); + JMP(asm_.dispatcher, true); } - // No blocklinking. - MOV(32, M(&mips_->pc), Imm32(destination)); - JMP(asm_.dispatcher, true); } void Jit::WriteExitDestInEAX() diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 22b0b2f498..7155dce860 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -24,8 +24,8 @@ LOCAL_MODULE := ppsspp_jni NATIVE := ../../native SRC := ../.. -LOCAL_CFLAGS := -DUSE_PROFILER -DARM -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -O2 -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math -LOCAL_CPPFLAGS := -std=gnu++0x +LOCAL_CFLAGS := -DUSE_PROFILER -DARM -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -g -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math +LOCAL_CXXFLAGS := -std=gnu++0x LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../../Common \ $(LOCAL_PATH)/../.. \ From 76481a300c057b67b913a401fc2efaac6e72032b Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 8 Jan 2013 23:52:11 +0100 Subject: [PATCH 16/49] Icache must be invalidated. Jit now starts to run, but there's no cube in cube.elf! --- Common/ArmEmitter.cpp | 22 +--- Common/ArmEmitter.h | 31 ++---- Common/MemArena.cpp | 6 +- Common/x64Emitter.cpp | 180 +------------------------------- Common/x64Emitter.h | 37 ------- Core/CoreTiming.cpp | 1 - Core/MIPS/ARM/ArmAsm.cpp | 32 ++++-- Core/MIPS/ARM/ArmAsm.h | 8 +- Core/MIPS/ARM/ArmCompBranch.cpp | 21 ++-- Core/MIPS/ARM/ArmJit.cpp | 14 ++- Core/MIPS/ARM/ArmRegCache.cpp | 2 +- Core/MIPS/x86/Asm.cpp | 2 + 12 files changed, 62 insertions(+), 294 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index adcf07cb94..72b8040b2b 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -59,7 +59,7 @@ const u8 *ARMXEmitter::AlignCodePage() return code; } -void ARMXEmitter::Flush() +void ARMXEmitter::FlushIcache() { __builtin___clear_cache (startcode, code); SLEEP(0); @@ -166,7 +166,7 @@ void ARMXEmitter::B (const void *fnptr) void ARMXEmitter::B(ARMReg src) { - Write32(condition | (18 << 20) | (0xFFF << 8) | (1 << 4) | src); + Write32(condition | 0x12FFF10 | src); } void ARMXEmitter::BL(const void *fnptr) @@ -536,20 +536,4 @@ void ARMXEmitter::VMOV(ARMReg Dest, ARMReg Src) } } -// helper routines for setting pointers -void ARMXEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2) -{ -} - -void ARMXEmitter::CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) -{ -} - -void ARMXEmitter::CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) -{ -} - -void ARMXEmitter::CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) -{ -} -} +} \ No newline at end of file diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index f3b1b4ad17..d865aeba52 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -204,17 +204,17 @@ public: { switch(Type) { - case TYPE_IMM: + case TYPE_IMM: return Imm12Mod(); // This'll need to be changed later - case TYPE_REG: + case TYPE_REG: return Rm(); - case TYPE_IMMSREG: + case TYPE_IMMSREG: return IMMSR(); - case TYPE_RSR: + case TYPE_RSR: return RSR(); - default: - _assert_msg_(DYNA_REC, false, "GetData with Invalid Type"); - break; + default: + _assert_msg_(DYNA_REC, false, "GetData with Invalid Type"); + return 0; } } const u32 IMMSR() // IMM shifted register @@ -344,7 +344,7 @@ public: const u8 *AlignCode16(); const u8 *AlignCodePage(); const u8 *GetCodePtr() const; - void Flush(); + void FlushIcache(); u8 *GetWritableCodePtr(); void SetCC(CCFlags cond = CC_AL); @@ -463,8 +463,6 @@ public: void VMOV(ARMReg Dest, ARMReg Src); // Utility functions - // The difference between this and CALL is that this aligns the stack - // where appropriate. void ARMABI_CallFunction(void *func); void ARMABI_CallFunctionC(void *func, u32 Arg0); void ARMABI_CallFunctionCNoSave(void *func, u32 Arg0); @@ -480,19 +478,6 @@ public: void UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval); - - // Strange call wrappers. - void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2); - void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3); - void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); - void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5); - #define CallCdeclFunction3_I(a,b,c,d) CallCdeclFunction3((void *)(a), (b), (c), (d)) - #define CallCdeclFunction4_I(a,b,c,d,e) CallCdeclFunction4((void *)(a), (b), (c), (d), (e)) - #define CallCdeclFunction5_I(a,b,c,d,e,f) CallCdeclFunction5((void *)(a), (b), (c), (d), (e), (f)) - #define CallCdeclFunction6_I(a,b,c,d,e,f,g) CallCdeclFunction6((void *)(a), (b), (c), (d), (e), (f), (g)) - - #define DECLARE_IMPORT(x) - }; // class ARMXEmitter diff --git a/Common/MemArena.cpp b/Common/MemArena.cpp index f7ad48565c..7487a6a53a 100644 --- a/Common/MemArena.cpp +++ b/Common/MemArena.cpp @@ -243,13 +243,13 @@ u8* MemArena::Find4GBBase() // We are unable to use relative addresses due to lack of mmap() return NULL; #else - void* base = mmap(0, 0x5000000, PROT_READ | PROT_WRITE, + void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1, 0); if (base == MAP_FAILED) { - PanicAlert("Failed to map 100 MB of memory space: %s", strerror(errno)); + PanicAlert("Failed to map 256 MB of memory space: %s", strerror(errno)); return 0; } - munmap(base, 0x5000000); + munmap(base, 0x10000000); return static_cast(base); #endif #endif diff --git a/Common/x64Emitter.cpp b/Common/x64Emitter.cpp index 7c0d88320c..a423f03762 100644 --- a/Common/x64Emitter.cpp +++ b/Common/x64Emitter.cpp @@ -1465,184 +1465,6 @@ void XEmitter::FWAIT() } void XEmitter::RTDSC() { Write8(0x0F); Write8(0x31); } - -// helper routines for setting pointers -void XEmitter::CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2) -{ - using namespace Gen; -#ifdef _M_X64 -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - CALL(fnptr); -#endif +} // Gen -#else - ABI_AlignStack(3 * 4); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(3 * 4); -#endif -#endif -} - -void XEmitter::CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(4 * 4); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(4 * 4); -#endif -#endif -} - -void XEmitter::CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - MOV(32, R(R8), Imm32(arg4)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(5 * 4); - PUSH(32, Imm32(arg4)); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(5 * 4); -#endif -#endif -} - -void XEmitter::CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - MOV(32, R(R8), Imm32(arg4)); - MOV(32, R(R9), Imm32(arg5)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(6 * 4); - PUSH(32, Imm32(arg5)); - PUSH(32, Imm32(arg4)); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(6 * 4); -#endif -#endif -} - -#ifdef _M_X64 - -// See header -void XEmitter::___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - CALLptr(M(impptr)); -} -void XEmitter::___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - CALLptr(M(impptr)); -} -void XEmitter::___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - CALLptr(M(impptr)); -} -void XEmitter::___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); - CALLptr(M(impptr)); -} - -#endif - -} diff --git a/Common/x64Emitter.h b/Common/x64Emitter.h index 816a82e90a..21a1deaeae 100644 --- a/Common/x64Emitter.h +++ b/Common/x64Emitter.h @@ -687,43 +687,6 @@ public: #else inline int ABI_GetNumXMMRegs() { return 16; } #endif - - // Strange call wrappers. - void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2); - void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3); - void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); - void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5); - -#if defined(_M_IX86) - - #define CallCdeclFunction3_I(a,b,c,d) CallCdeclFunction3((void *)(a), (b), (c), (d)) - #define CallCdeclFunction4_I(a,b,c,d,e) CallCdeclFunction4((void *)(a), (b), (c), (d), (e)) - #define CallCdeclFunction5_I(a,b,c,d,e,f) CallCdeclFunction5((void *)(a), (b), (c), (d), (e), (f)) - #define CallCdeclFunction6_I(a,b,c,d,e,f,g) CallCdeclFunction6((void *)(a), (b), (c), (d), (e), (f), (g)) - - #define DECLARE_IMPORT(x) - -#else - - // Comments from VertexLoader.cpp about these horrors: - - // This is a horrible hack that is necessary in 64-bit mode because Opengl32.dll is based way, way above the 32-bit - // address space that is within reach of a CALL, and just doing &fn gives us these high uncallable addresses. So we - // want to grab the function pointers from the import table instead. - - void ___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2); - void ___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3); - void ___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4); - void ___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5); - - #define CallCdeclFunction3_I(a,b,c,d) ___CallCdeclImport3(&__imp_##a,b,c,d) - #define CallCdeclFunction4_I(a,b,c,d,e) ___CallCdeclImport4(&__imp_##a,b,c,d,e) - #define CallCdeclFunction5_I(a,b,c,d,e,f) ___CallCdeclImport5(&__imp_##a,b,c,d,e,f) - #define CallCdeclFunction6_I(a,b,c,d,e,f,g) ___CallCdeclImport6(&__imp_##a,b,c,d,e,f,g) - - #define DECLARE_IMPORT(x) extern "C" void *__imp_##x - -#endif }; // class XEmitter diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 0fbad2a7fb..70a9431f5c 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -462,7 +462,6 @@ void MoveEvents() void Advance() { - WARN_LOG(HLE, "ADVANCE!"); int cyclesExecuted = slicelength - downcount; globalTimer += cyclesExecuted; downcount = slicelength; diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 2e7691ba35..70b792602d 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -65,9 +65,9 @@ void Jit() MIPSComp::jit->Compile(currentMIPS->pc); } -void ShowPC() { +void ShowPC(u32 sp) { if (currentMIPS) { - WARN_LOG(HLE, "PC : %08x", currentMIPS->pc); + WARN_LOG(HLE, "PC : %08x ArmSP : %08x", currentMIPS->pc, sp); } else { ERROR_LOG(HLE, "Universe corrupt?"); } @@ -79,6 +79,13 @@ void DisassembleArm(const u8 *data, int size); // dynarec buffer // At this offset - 4, there is an int specifying the block number. +void ArmAsmRoutineManager::QuickCallFunction(ARMReg reg, void *func) { + PUSH(1, _LR); + ARMABI_MOVI2R(reg, (u32)(func)); + BL(reg); + POP(1, _LR); +} + void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) { enterCode = AlignCode16(); @@ -93,10 +100,10 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // R13 cannot be used as it's the stack pointer. ARMABI_MOVI2R(R11, (u32)Memory::base); ARMABI_MOVI2R(R10, (u32)mips); - ARMABI_MOVI2R(R9, (u32)jit->GetBlockCache()->GetCodePointers()); + ARMABI_MOVI2R(R7, (u32)jit->GetBlockCache()->GetCodePointers()); outerLoop = GetCodePtr(); - ARMABI_CallFunction((void *)&CoreTiming::Advance); + QuickCallFunction(R0, (void *)&CoreTiming::Advance); FixupBranch skipToRealDispatch = B(); //skip the sync and compare first time dispatcherCheckCoreState = GetCodePtr(); @@ -106,9 +113,9 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) LDR(R0, R0); CMP(R0, 0); FixupBranch badCoreState = B_CC(CC_NEQ); - - // At this point : flags = EQ. Fine for the next check, no need to jump over it. + FixupBranch skipToRealDispatch2 = B(); //skip the sync and compare first time + // 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 @@ -116,11 +123,17 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) FixupBranch bail = B_CC(CC_MI); SetJumpTarget(skipToRealDispatch); + SetJumpTarget(skipToRealDispatch2); dispatcherNoCheck = GetCodePtr(); // Debug - // ARMABI_CallFunction((void *)&ShowPC); + // MOV(R0, R13); + // QuickCallFunction(R1, (void *)&ShowPC); + + ARMABI_MOVI2R(R7, (u32)jit->GetBlockCache()->GetCodePointers()); + ARMABI_MOVI2R(R11, (u32)Memory::base); + ARMABI_MOVI2R(R10, (u32)mips); LDR(R0, R10, offsetof(MIPSState, pc)); @@ -138,7 +151,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) //ADD(32, M(&mips->debugCount), Imm8(1)); } // grab from list and jump to it - ADD(R0, R9, Operand2(2, ST_LSL, R0)); + ADD(R0, R7, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); @@ -166,4 +179,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) INFO_LOG(HLE, "THE DISASM ========================"); DisassembleArm(enterCode, GetCodePtr() - enterCode); INFO_LOG(HLE, "END OF THE DISASM ========================"); + + // Don't forget to zap the instruction cache! + FlushIcache(); } \ No newline at end of file diff --git a/Core/MIPS/ARM/ArmAsm.h b/Core/MIPS/ARM/ArmAsm.h index faba14a49d..14af4e2dcd 100644 --- a/Core/MIPS/ARM/ArmAsm.h +++ b/Core/MIPS/ARM/ArmAsm.h @@ -30,10 +30,6 @@ namespace MIPSComp class ArmAsmRoutineManager : public ArmGen::ARMXCodeBlock { -private: - void Generate(MIPSState *mips, MIPSComp::Jit *jit); - void GenerateCommon(); - public: ArmAsmRoutineManager() { @@ -58,6 +54,10 @@ public: const u8 *dispatcherNoCheck; const u8 *breakpointBailout; + +private: + void Generate(MIPSState *mips, MIPSComp::Jit *jit); + void QuickCallFunction(ArmGen::ARMReg reg, void *func); }; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 75a1739ff7..89fd1035c8 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -62,20 +62,20 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) //ERROR_LOG(CPU, "Not nice delay slot in BranchRSRTComp :( %08x", js.compilerPC); } // The delay slot being nice doesn't really matter though... - /* + if (rt == 0) { gpr.MapReg(rs, MAP_INITVAL); CMP(gpr.R(rs), Operand2(0)); - }*/ + } /* else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' { gpr.MapReg(rt, MAP_INITVAL); CMP(gpr.R(rt), Operand2(0)); } - else*/ - { + */ + else { gpr.SpillLock(rs, rt); gpr.MapReg(rs, MAP_INITVAL); gpr.MapReg(rt, MAP_INITVAL); @@ -227,9 +227,8 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) } FlushAll(); - ARMABI_MOVI2R(R0, (u32)&(mips_->fpcond)); - LDR(R0, R0, Operand2(0, TYPE_IMM)); - TST(R0, Operand2(1, TYPE_IMM)); + LDR(R0, R10, offsetof(MIPSState, fpcond)); + CMP(R0, Operand2(1, TYPE_IMM)); ArmGen::FixupBranch ptr; js.inDelaySlot = true; if (!likely) @@ -263,10 +262,10 @@ void Jit::Comp_FPUBranch(u32 op) { switch((op >> 16) & 0x1f) { - case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f - case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t - case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl - case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl + case 0: BranchFPFlag(op, CC_EQ, false); break; // bc1f + case 1: BranchFPFlag(op, CC_NEQ, false); break; // bc1t + case 2: BranchFPFlag(op, CC_EQ, true); break; // bc1fl + case 3: BranchFPFlag(op, CC_NEQ, true); break; // bc1tl default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); break; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 1b17ef6d55..24373049b3 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -36,7 +36,7 @@ void DisassembleArm(const u8 *data, int size) { // MAGIC SPECIAL CASE for MOVW/MOVT readability! if ((inst & 0x0FF00000) == 0x03000000 && (next & 0x0FF00000) == 0x03400000) { u32 low = ((inst & 0x000F0000) >> 4) | (inst & 0x0FFF); - u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); + u32 hi = ((next & 0x000F0000) >> 4) | (next & 0x0FFF); int reg0 = (inst & 0x0000F000) >> 12; int reg1 = (next & 0x0000F000) >> 12; if (reg0 == reg1) { @@ -76,8 +76,6 @@ void Jit::ClearCache() ClearCodeSpace(); } -u8 *codeCache; -#define CACHESIZE 16384*1024 void Jit::CompileAt(u32 addr) { u32 op = Memory::Read_Instruction(addr); @@ -86,7 +84,7 @@ void Jit::CompileAt(u32 addr) void Jit::Compile(u32 em_address) { - //ERROR_LOG(CPU, "Compile %08x", em_address); + ERROR_LOG(CPU, "Compile %08x", em_address); if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) { ClearCache(); @@ -105,10 +103,6 @@ void Jit::RunLoopUntil(u64 globalticks) INFO_LOG(DYNA_REC, "or Two!"); } -void Hullo(int a, int b, int c, int d) { - INFO_LOG(DYNA_REC, "Hullo %08x %08x %08x %08x", a, b, c, d); -} - const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { js.cancel = false; @@ -165,6 +159,10 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) DisassembleArm(b->checkedEntry, GetCodePtr() - b->checkedEntry); #endif AlignCode16(); + + // Don't forget to zap the instruction cache! + FlushIcache(); + b->originalSize = numInstructions; return b->normalEntry; } diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 538e269cc2..e1f0637f15 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -50,7 +50,7 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) { // R9 and upwards are reserved for jit basics. // Six allocated registers should be enough... static const ARMReg allocationOrder[] = { - R2, R3, R4, R5, R6, R7 + R2, R3, R4, R5, R6 //, R7 }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; diff --git a/Core/MIPS/x86/Asm.cpp b/Core/MIPS/x86/Asm.cpp index 5d3bc6ff28..36816f24b4 100644 --- a/Core/MIPS/x86/Asm.cpp +++ b/Core/MIPS/x86/Asm.cpp @@ -83,6 +83,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) CMP(32, M((void*)&coreState), Imm32(0)); FixupBranch badCoreState = J_CC(CC_NZ, true); + FixupBranch skipToRealDispatch2 = J(); //skip the sync and compare first time dispatcher = GetCodePtr(); // The result of slice decrementation should be in flags if somebody jumped here @@ -90,6 +91,7 @@ void AsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) FixupBranch bail = J_CC(CC_S, true); SetJumpTarget(skipToRealDispatch); + SetJumpTarget(skipToRealDispatch2); dispatcherNoCheck = GetCodePtr(); From f5c94775b9b8a39acf88ee7f26d4b04d06e2c4d5 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 00:12:02 +0100 Subject: [PATCH 17/49] Cleanup --- Core/Core.cpp | 2 -- Core/HLE/sceDisplay.cpp | 1 - Core/MIPS/ARM/ArmAsm.cpp | 7 ++++--- Core/MIPS/ARM/ArmCompBranch.cpp | 22 ++++++++++++---------- Core/MIPS/ARM/ArmJit.cpp | 3 --- Core/MIPS/x86/CompBranch.cpp | 6 ------ Windows/main.cpp | 3 --- android/jni/NativeApp.cpp | 1 + 8 files changed, 17 insertions(+), 28 deletions(-) diff --git a/Core/Core.cpp b/Core/Core.cpp index 4b7e25f41e..49f7846bb4 100644 --- a/Core/Core.cpp +++ b/Core/Core.cpp @@ -65,7 +65,6 @@ bool Core_IsStepping() void Core_RunLoop() { currentMIPS->RunLoopUntil(0xFFFFFFFFFFFFFFFULL); - INFO_LOG(DYNA_REC, "Wooo"); } void Core_DoSingleStep() @@ -94,7 +93,6 @@ reswitch: case CORE_RUNNING: //1: enter a fast runloop Core_RunLoop(); - INFO_LOG(DYNA_REC, "Yay"); break; // We should never get here on Android. diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 1c9c085a99..e85be05e2c 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -269,7 +269,6 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { // Tell the emu core that it's time to stop emulating // Win32 doesn't need this. #ifndef _WIN32 - ERROR_LOG(HLE, "End oph phrame"); coreState = CORE_NEXTFRAME; #endif } diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 70b792602d..977e56d19c 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -176,9 +176,10 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns - INFO_LOG(HLE, "THE DISASM ========================"); - DisassembleArm(enterCode, GetCodePtr() - enterCode); - INFO_LOG(HLE, "END OF THE DISASM ========================"); + // Uncomment if you want to see the output... + // INFO_LOG(HLE, "THE DISASM ========================"); + // DisassembleArm(enterCode, GetCodePtr() - enterCode); + // INFO_LOG(HLE, "END OF THE DISASM ========================"); // Don't forget to zap the instruction cache! FlushIcache(); diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 89fd1035c8..8485a0c1a3 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -63,19 +63,21 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) } // The delay slot being nice doesn't really matter though... - if (rt == 0) + /* + if (rt == 0) { gpr.MapReg(rs, MAP_INITVAL); - CMP(gpr.R(rs), Operand2(0)); + CMP(gpr.R(rs), Operand2(0, TYPE_IMM)); } - /* else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' { gpr.MapReg(rt, MAP_INITVAL); CMP(gpr.R(rt), Operand2(0)); } + + else */ - else { + { gpr.SpillLock(rs, rt); gpr.MapReg(rs, MAP_INITVAL); gpr.MapReg(rt, MAP_INITVAL); @@ -135,7 +137,7 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) // ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC); } gpr.MapReg(rs, MAP_INITVAL); - CMP(gpr.R(rs), Operand2(0)); + CMP(gpr.R(rs), Operand2(0, TYPE_IMM)); FlushAll(); ArmGen::FixupBranch ptr; @@ -228,7 +230,7 @@ void Jit::BranchFPFlag(u32 op, ArmGen::CCFlags cc, bool likely) FlushAll(); LDR(R0, R10, offsetof(MIPSState, fpcond)); - CMP(R0, Operand2(1, TYPE_IMM)); + TST(R0, Operand2(1, TYPE_IMM)); ArmGen::FixupBranch ptr; js.inDelaySlot = true; if (!likely) @@ -262,10 +264,10 @@ void Jit::Comp_FPUBranch(u32 op) { switch((op >> 16) & 0x1f) { - case 0: BranchFPFlag(op, CC_EQ, false); break; // bc1f - case 1: BranchFPFlag(op, CC_NEQ, false); break; // bc1t - case 2: BranchFPFlag(op, CC_EQ, true); break; // bc1fl - case 3: BranchFPFlag(op, CC_NEQ, true); break; // bc1tl + case 0: BranchFPFlag(op, CC_NEQ, false); break; // bc1f + case 1: BranchFPFlag(op, CC_EQ, false); break; // bc1t + case 2: BranchFPFlag(op, CC_NEQ, true); break; // bc1fl + case 3: BranchFPFlag(op, CC_EQ, true); break; // bc1tl default: _dbg_assert_msg_(CPU,0,"Trying to interpret instruction that can't be interpreted"); break; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 24373049b3..671112c24b 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -84,7 +84,6 @@ void Jit::CompileAt(u32 addr) void Jit::Compile(u32 em_address) { - ERROR_LOG(CPU, "Compile %08x", em_address); if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) { ClearCache(); @@ -99,8 +98,6 @@ void Jit::RunLoopUntil(u64 globalticks) { // TODO: copy globalticks somewhere ((void (*)())asm_.enterCode)(); - INFO_LOG(DYNA_REC, "Left asm code like a boss!"); - INFO_LOG(DYNA_REC, "or Two!"); } const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) diff --git a/Core/MIPS/x86/CompBranch.cpp b/Core/MIPS/x86/CompBranch.cpp index 64a85d0740..f9e95e8451 100644 --- a/Core/MIPS/x86/CompBranch.cpp +++ b/Core/MIPS/x86/CompBranch.cpp @@ -343,11 +343,6 @@ void Jit::BranchVFPUFlag(u32 op, Gen::CCFlags cc, bool likely) void Jit::Comp_VBranch(u32 op) { - // _dbg_assert_msg_(CPU,0,"comp_vbranch not supported"); - - //int imm = (signed short)(op&0xFFFF)<<2; - //u32 targetAddr = js.compilerPC + imm + 4; - switch ((op >> 16) & 3) { case 0: BranchVFPUFlag(op, CC_NZ, false); break; //bvf @@ -369,7 +364,6 @@ void Jit::Comp_Jump(u32 op) } u32 off = ((op & 0x3FFFFFF) << 2); u32 targetAddr = (js.compilerPC & 0xF0000000) | off; - //Delay slot CompileAt(js.compilerPC + 4); FlushAll(); diff --git a/Windows/main.cpp b/Windows/main.cpp index 2b5ddc1db9..c875f0c7b2 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -51,9 +51,6 @@ CMemoryDlg *memoryWindow[MAX_CPUCOUNT]; int WINAPI WinMain(HINSTANCE _hInstance, HINSTANCE hPrevInstance, LPSTR szCmdLine, int iCmdShow) { - char temp[256]; - ArmDis(0, 0xE12fff10, temp); - Common::EnableCrashingOnCrashes(); const char *fileToStart = NULL; diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 995342c7a9..bd7f797d78 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -192,6 +192,7 @@ void NativeInit(int argc, const char *argv[], const char *savegame_directory, co switch (argv[i][1]) { case 'd': // Enable debug logging + // Note that you must also change the max log level in Log.h. logLevel = LogTypes::LDEBUG; break; case 'g': From 81c6c4805d954a445888f5084743533016fd011e Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 00:12:38 +0100 Subject: [PATCH 18/49] Small dispatcher optimizations, cleanup. Still no cube. --- Core/MIPS/ARM/ArmAsm.cpp | 22 +++++----------------- Core/MIPS/ARM/ArmCompALU.cpp | 2 +- Core/MIPS/ARM/ArmRegCache.cpp | 2 +- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 977e56d19c..34604e497e 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -100,7 +100,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // R13 cannot be used as it's the stack pointer. ARMABI_MOVI2R(R11, (u32)Memory::base); ARMABI_MOVI2R(R10, (u32)mips); - ARMABI_MOVI2R(R7, (u32)jit->GetBlockCache()->GetCodePointers()); + ARMABI_MOVI2R(R9, (u32)jit->GetBlockCache()->GetCodePointers()); outerLoop = GetCodePtr(); QuickCallFunction(R0, (void *)&CoreTiming::Advance); @@ -131,31 +131,19 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // MOV(R0, R13); // QuickCallFunction(R1, (void *)&ShowPC); - ARMABI_MOVI2R(R7, (u32)jit->GetBlockCache()->GetCodePointers()); - ARMABI_MOVI2R(R11, (u32)Memory::base); - ARMABI_MOVI2R(R10, (u32)mips); - LDR(R0, R10, offsetof(MIPSState, pc)); - - ARMABI_MOVI2R(R1, Memory::MEMVIEW32_MASK); // can be done with single MOVN instruction - AND(R0, R0, R1); + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF ADD(R0, R0, R11); // TODO: Optimize (can merge with next instr) LDR(R0, R0); AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); - FixupBranch notfound = B_CC(CC_NEQ); // TODO : No need for a branch really, can use CCs. + SetCC(CC_EQ); // IDEA - we have 24 bits, why not just use offsets from base of code? - if (enableDebug) - { - //ADD(32, M(&mips->debugCount), Imm8(1)); - } - // grab from list and jump to it - ADD(R0, R7, Operand2(2, ST_LSL, R0)); + ADD(R0, R9, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); - - SetJumpTarget(notfound); + SetCC(CC_AL); //Ok, no block, let's jit ARMABI_CallFunction((void *)&Jit); diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index f9de2ef4ab..28236a77e3 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -124,11 +124,11 @@ namespace MIPSComp gpr.UnlockAll(); break; + */ case 15: //R(rt) = uimm << 16; break; //lui gpr.SetImm(rt, uimm << 16); break; - */ default: Comp_Generic(op); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index e1f0637f15..538e269cc2 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -50,7 +50,7 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) { // R9 and upwards are reserved for jit basics. // Six allocated registers should be enough... static const ARMReg allocationOrder[] = { - R2, R3, R4, R5, R6 //, R7 + R2, R3, R4, R5, R6, R7 }; count = sizeof(allocationOrder) / sizeof(const int); return allocationOrder; From ea3055322c324e81c7ad071634f904ad0e060143 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 00:45:54 +0100 Subject: [PATCH 19/49] Oops --- Core/MIPS/x86/Jit.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index fa7052093d..90553222a4 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -57,7 +57,6 @@ void Jit::CompileAt(u32 addr) void Jit::Compile(u32 em_address) { - ERROR_LOG(CPU, "Compile %08x", em_address); if (GetSpaceLeft() < 0x10000 || blocks.IsFull()) { ClearCache(); From dafc9f62df2d57d16ed0f7e99d2524230a619ef0 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 11:20:48 +0100 Subject: [PATCH 20/49] Regcache fixes, etc. thing still don't work when I turn on addiu :( --- Common/ArmEmitter.cpp | 27 +++++++++++++++++ Common/ArmEmitter.h | 13 ++++++--- Core/HLE/sceMpeg.cpp | 7 +++-- Core/MIPS/ARM/ArmCompALU.cpp | 52 ++++++++++++++++++++------------- Core/MIPS/ARM/ArmCompBranch.cpp | 3 -- Core/MIPS/ARM/ArmCompFPU.cpp | 2 ++ Core/MIPS/ARM/ArmJit.cpp | 23 +++++++++++---- Core/MIPS/ARM/ArmJit.h | 2 +- Core/MIPS/ARM/ArmRegCache.cpp | 33 ++++++++++++--------- Core/MIPS/MIPS.h | 1 + 10 files changed, 112 insertions(+), 51 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index ad7651d968..1ca0d36429 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -29,6 +29,33 @@ namespace ArmGen { +inline u32 RotR(u32 a, int amount) { + return (a >> amount) | (a << (31 - amount)); +} + +bool TryMakeOperand2(u32 imm, Operand2 &op2) { + // Just brute force it. + for (int i = 0; i < 16; i++) { + if ((imm & 0xFF) == imm) { + op2 = Operand2((u8)imm, (u8)i); + return true; + } + imm = RotR(imm, 2); + } + return false; +} + +bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse) +{ + if (!TryMakeOperand2(imm, op2)) { + *inverse = true; + return TryMakeOperand2(~imm, op2); + } else { + *inverse = false; + return true; + } +} + void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index d865aeba52..b3a8a1b613 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -112,7 +112,6 @@ enum OpType TYPE_MEM }; - // This is no longer a proper operand2 class. Need to split up. class Operand2 { @@ -298,6 +297,12 @@ public: }; + + +bool TryMakeOperand2(u32 imm, Operand2 &op2); +bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse); + + inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); } inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); } @@ -401,7 +406,7 @@ public: void LSLS(ARMReg dest, ARMReg src, ARMReg op2); void SBC (ARMReg dest, ARMReg src, Operand2 op2); void SBCS(ARMReg dest, ARMReg src, Operand2 op2); - void REV (ARMReg dest, ARMReg src ); + void REV (ARMReg dest, ARMReg src); void RSC (ARMReg dest, ARMReg src, Operand2 op2); void RSCS(ARMReg dest, ARMReg src, Operand2 op2); void TST ( ARMReg src, Operand2 op2); @@ -429,9 +434,9 @@ public: void SXTB(ARMReg dest, ARMReg op2); void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0); void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0); - // Using just MSR here messes with our defines on the PPC side of stuff + // Using just MSR here messes with our defines on the PPC side of stuff (when this code was in dolphin...) // Just need to put an underscore here, bit annoying. - void _MSR (bool nzcvq, bool g, Operand2 op2); + void _MSR (bool nzcvq, bool g, Operand2 op2); void _MSR (bool nzcvq, bool g, ARMReg src ); void MRS (ARMReg dest); diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 6c1f57b92b..f5c81ff5ec 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -718,7 +718,7 @@ u32 sceMpegUnRegistStream(u32 mpeg, int streamUid) return -1; } - StreamInfo info; + StreamInfo info = {0}; switch (info.type) { case MPEG_AVC_STREAM: @@ -1051,9 +1051,10 @@ u32 sceMpegChangeGetAuMode(u32 mpeg, int streamUid, int mode) return -1; } - StreamInfo info; + // NOTE: Where is the info supposed to come from? + StreamInfo info = {0}; info.sid = streamUid; - if (info.sid) { + if (info.sid) { switch (info.type) { case MPEG_AVC_STREAM: if(mode == MPEG_AU_MODE_DECODE) { diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 28236a77e3..281c027cde 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -34,20 +34,25 @@ using namespace MIPSAnalyst; namespace MIPSComp { - /* - void Jit::CompImmLogic(u32 op, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2)) + u32 EvalOR(u32 a, u32 b) { return a | b; } + u32 EvalXOR(u32 a, u32 b) { return a ^ b; } + u32 EvalAND(u32 a, u32 b) { return a & b; } + + + void Jit::CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)) { - u32 uimm = (u16)(op & 0xFFFF); - gpr.SpillLock() - int rt = _RT; - int rs = _RS; - gpr.Lock(rt, rs); - gpr.BindToRegister(rt, rt == rs, true); - if (rt != rs) - MOV(32, gpr.R(rt), gpr.R(rs)); - (this->*arith)(32, gpr.R(rt), Imm32(uimm)); - gpr.UnlockAll(); - }*/ + if (gpr.IsImm(rs)) { + gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm)); + } else { + gpr.SpillLock(rs, rt); + gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); + gpr.MapReg(rs, MAP_INITVAL); + gpr.ReleaseSpillLocks(); + // TODO: Special case when uimm can be represented as an Operand2 + ARMABI_MOVI2R(R0, (u32)uimm); + (this->*arith)(gpr.R(rt), gpr.R(rs), R0); + } + } void Jit::Comp_IType(u32 op) { @@ -59,26 +64,31 @@ namespace MIPSComp switch (op >> 26) { - /* case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { - if (gpr.IsImm(rs)) - { + if (gpr.IsImm(rs)) { gpr.SetImm(rt, gpr.GetImm(rs) + simm); - break; - } else if (rs == 0) { + }/* else if (rs == 0) { gpr.SetImm(rt, simm); } else { gpr.SpillLock(rs, rt); gpr.MapReg(rs, MAP_INITVAL); gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); - ARMABI_MOVI2R(R0, (u32)simm); - ADD(gpr.R(rt), gpr.R(rs), R0); + Operand2 op2; + if (false && TryMakeOperand2(simm, op2)) { + ADD(gpr.R(rt), gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)simm); + ADD(gpr.R(rt), gpr.R(rs), R0); + } gpr.ReleaseSpillLocks(); + }*/ + else { + Comp_Generic(op); } break; - }*/ + } /* case 13: // OR { diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 8485a0c1a3..7a4deaed8d 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -63,7 +63,6 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) } // The delay slot being nice doesn't really matter though... - /* if (rt == 0) { gpr.MapReg(rs, MAP_INITVAL); @@ -74,9 +73,7 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) gpr.MapReg(rt, MAP_INITVAL); CMP(gpr.R(rt), Operand2(0)); } - else - */ { gpr.SpillLock(rs, rt); gpr.MapReg(rs, MAP_INITVAL); diff --git a/Core/MIPS/ARM/ArmCompFPU.cpp b/Core/MIPS/ARM/ArmCompFPU.cpp index d008d2308b..9b3dee1bc4 100644 --- a/Core/MIPS/ARM/ArmCompFPU.cpp +++ b/Core/MIPS/ARM/ArmCompFPU.cpp @@ -115,6 +115,7 @@ void Jit::Comp_FPULS(u32 op) void Jit::Comp_FPU2op(u32 op) { + OLDD int fs = _FS; int fd = _FD; @@ -173,6 +174,7 @@ void Jit::Comp_FPU2op(u32 op) void Jit::Comp_mxc1(u32 op) { + OLDD int fs = _FS; int rt = _RT; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 671112c24b..7ff148aa1c 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -127,7 +127,11 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) int numInstructions = 0; int cycles = 0; -// #define LOGASM + + static int logBlocks = 20; + if (logBlocks > 0) logBlocks--; + +#define LOGASM #ifdef LOGASM char temp[256]; #endif @@ -135,8 +139,10 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) { u32 inst = Memory::Read_Instruction(js.compilerPC); #ifdef LOGASM - MIPSDisAsm(inst, js.compilerPC, temp, true); - INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); + if (logBlocks > 0) { + MIPSDisAsm(inst, js.compilerPC, temp, true); + INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); + } #endif js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); @@ -146,14 +152,19 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) numInstructions++; } #ifdef LOGASM - MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); - INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); + if (logBlocks > 0) { + MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); + INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); + } #endif b->codeSize = GetCodePtr() - b->normalEntry; #ifdef LOGASM - DisassembleArm(b->checkedEntry, GetCodePtr() - b->checkedEntry); + if (logBlocks > 0) { + INFO_LOG(DYNA_REC, "=============== ARM ==============="); + DisassembleArm(b->normalEntry, GetCodePtr() - b->normalEntry); + } #endif AlignCode16(); diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 4f70ea53ed..805f44da1b 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -110,8 +110,8 @@ private: void BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely); // Utilities to reduce duplicated code + void CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)); /* - void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2)); void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompTriArith(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompShiftImm(u32 op, void (ARMXEmitter::*shift)(int, OpArg, OpArg)); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 538e269cc2..7c34066b83 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -57,17 +57,20 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) { } ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { - // Let's see if it's already mapped. - for (int i = 0; i < NUM_ARMREG; i++) { - if (ar[i].mipsReg == mipsReg) { - if (mapFlags & MAP_DIRTY) - ar[i].isDirty = true; - // Already mapped, no need to do anything more. - return (ARMReg)i; + // Let's see if it's already mapped. If so we just need to update the dirty flag. + // We don't need to check for ML_INITVAL because we assume that anyone who maps + // with that flag immediately writes a "known" value to the register. + if (mr[mipsReg].loc == ML_ARMREG) { + if (ar[mr[mipsReg].reg].mipsReg != mipsReg) { + ERROR_LOG(HLE, "Mapping out of sync! %i", mipsReg); } + if (mapFlags & MAP_DIRTY) { + ar[mr[mipsReg].reg].isDirty = true; + } + return mr[mipsReg].reg; } - // Okay, so we need to allocate one. + // Okay, not mapped, so we need to allocate an ARM register. int allocCount; const ARMReg *allocOrder = GetMIPSAllocationOrder(allocCount); @@ -114,6 +117,7 @@ void ArmRegCache::FlushArmReg(ARMReg r) { if (ar[r].mipsReg != -1) { if (ar[r].isDirty && mr[ar[r].mipsReg].loc == ML_ARMREG) emit->STR(CTXREG, r, 4 * ar[r].mipsReg); + // IMMs won't be in an ARM reg. mr[ar[r].mipsReg].loc = ML_MEM; } else { ERROR_LOG(HLE, "Dirty but no mipsreg?"); @@ -151,11 +155,12 @@ void ArmRegCache::FlushAll() { } void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { - // Zap existing value + // Zap existing value if cached in a reg if (mr[r].loc == ML_ARMREG) ar[mr[r].reg].mipsReg = -1; mr[r].loc = ML_IMM; mr[r].imm = immVal; + mr[r].reg = INVALID_REG; } bool ArmRegCache::IsImm(MIPSReg r) const { @@ -163,7 +168,9 @@ bool ArmRegCache::IsImm(MIPSReg r) const { } u32 ArmRegCache::GetImm(MIPSReg r) const { - // TODO: Check. + if (mr[r].loc != ML_IMM) { + ERROR_LOG(JIT, "Trying to get imm from non-imm register %i", r); + } return mr[r].imm; } @@ -176,8 +183,8 @@ int ArmRegCache::GetMipsRegOffset(MIPSReg r) { case MIPSREG_LO: return offsetof(MIPSState, lo); } - _dbg_assert_msg_(JIT, false, "bad mips register %i", (int)r); - return -999; // boom! + ERROR_LOG(JIT, "bad mips register %i", r); + return 0; // or what? } void ArmRegCache::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3) { @@ -187,7 +194,7 @@ void ArmRegCache::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3) { } void ArmRegCache::ReleaseSpillLocks() { - for (int i = 0; i < 16; i++) { + for (int i = 0; i < NUM_ARMREG; i++) { ar[i].spillLock = false; } } diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 4f769301fe..701c1afdbc 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -113,6 +113,7 @@ public: void Reset(); void DoState(PointerWrap &p); + // MUST start with r! u32 r[32]; float f[32]; float v[128]; From 17210c53645a472e70f21b6295a17e57cd06996c Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 11:52:49 +0100 Subject: [PATCH 21/49] Generate immediates as rot+shift when possible. --- Common/ArmABI.cpp | 32 +++++++++++++++++++------------- Common/ArmEmitter.cpp | 18 +++++++++++++----- Common/ArmEmitter.h | 2 +- Core/MIPS/ARM/ArmCompALU.cpp | 7 ++++--- 4 files changed, 37 insertions(+), 22 deletions(-) diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index 82dbd7b762..a854c3e983 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -24,14 +24,14 @@ using namespace ArmGen; void ARMXEmitter::ARMABI_CallFunction(void *func) { PUSH(5, R0, R1, R2, R3, _LR); - ARMABI_MOVI2R(R14, Mem(func)); + ARMABI_MOVI2R(R14, (u32)func); BL(R14); POP(5, R0, R1, R2, R3, _LR); } void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) { PUSH(5, R0, R1, R2, R3, _LR); - ARMABI_MOVI2R(R14, Mem(func)); + ARMABI_MOVI2R(R14, (u32)func); ARMABI_MOVI2R(R0, Arg); BL(R14); POP(5, R0, R1, R2, R3, _LR); @@ -40,7 +40,7 @@ void ARMXEmitter::ARMABI_CallFunctionC(void *func, u32 Arg) void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg) { PUSH(1, _LR); - ARMABI_MOVI2R(R14, Mem(func)); + ARMABI_MOVI2R(R14, (u32)func); ARMABI_MOVI2R(R0, Arg); BL(R14); POP(1, _LR); @@ -49,7 +49,7 @@ void ARMXEmitter::ARMABI_CallFunctionCNoSave(void *func, u32 Arg) void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) { PUSH(5, R0, R1, R2, R3, _LR); - ARMABI_MOVI2R(R14, Mem(func)); + ARMABI_MOVI2R(R14, (u32)func); ARMABI_MOVI2R(R0, Arg1); ARMABI_MOVI2R(R1, Arg2); BL(R14); @@ -59,7 +59,7 @@ void ARMXEmitter::ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2) void ARMXEmitter::ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3) { PUSH(5, R0, R1, R2, R3, _LR); - ARMABI_MOVI2R(R14, Mem(func)); + ARMABI_MOVI2R(R14, (u32)func); ARMABI_MOVI2R(R0, Arg1); ARMABI_MOVI2R(R1, Arg2); ARMABI_MOVI2R(R2, Arg3); @@ -76,14 +76,20 @@ void ARMXEmitter::ARMABI_PopAllCalleeSavedRegsAndAdjustStack() { POP(4, R0, R1, R2, R3); } -void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, Operand2 val) +void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, u32 val) { - // TODO: There are more fancy ways to save calls if we check if - // The imm can be rotated or shifted a certain way. - // Masks tend to be able to be moved in to a reg with one call - MOVW(reg, val); - if(val.Value & 0xFFFF0000) - MOVT(reg, val, true); + Operand2 op2; + bool inverse; + if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!inverse) + MOV(reg, op2); + else + MVN(reg, op2); + } else { + MOVW(reg, val); + if(val & 0xFFFF0000) + MOVT(reg, val, true); + } } // Moves IMM to memory location void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) @@ -124,7 +130,7 @@ void ARMXEmitter::UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval) // Can't update GE with the other ones with a immediate // Got to use a scratch register u32 Imm = (Flags << 27) | ((GEval & 0xF) << 16); - ARMABI_MOVI2R(R14, IMM(Imm)); + ARMABI_MOVI2R(R14, Imm); _MSR(true, true, R14); } else diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 1ca0d36429..e4d18b6921 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -30,17 +30,23 @@ namespace ArmGen { inline u32 RotR(u32 a, int amount) { - return (a >> amount) | (a << (31 - amount)); + if (!amount) return a; + return (a >> amount) | (a << (32 - amount)); +} + +inline u32 RotL(u32 a, int amount) { + if (!amount) return a; + return (a << amount) | (a >> (32 - amount)); } bool TryMakeOperand2(u32 imm, Operand2 &op2) { // Just brute force it. for (int i = 0; i < 16; i++) { - if ((imm & 0xFF) == imm) { - op2 = Operand2((u8)imm, (u8)i); + int mask = RotR(0xFF, i * 2); + if ((imm & mask) == imm) { + op2 = Operand2((u8)(RotL(imm, i * 2)), (u8)i); return true; } - imm = RotR(imm, 2); } return false; } @@ -48,6 +54,8 @@ bool TryMakeOperand2(u32 imm, Operand2 &op2) { bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse) { if (!TryMakeOperand2(imm, op2)) { + return false; + // TODO: Test properly. *inverse = true; return TryMakeOperand2(~imm, op2); } else { @@ -344,7 +352,7 @@ void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, b break; } } - if (op == -1) + if (op == (u32)-1) _assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType()); Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data); } diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index b3a8a1b613..45da845648 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -475,7 +475,7 @@ public: void ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3); void ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); void ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); - void ARMABI_MOVI2R(ARMReg reg, Operand2 val); + void ARMABI_MOVI2R(ARMReg reg, u32 val); void ARMABI_MOVI2M(Operand2 op, Operand2 val); void ARMABI_MOVI2M(u32 addr, Operand2 val); void ARMABI_ShowConditions(); diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 281c027cde..36ea208cd4 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -64,12 +64,13 @@ namespace MIPSComp switch (op >> 26) { + /* case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { if (gpr.IsImm(rs)) { gpr.SetImm(rt, gpr.GetImm(rs) + simm); - }/* else if (rs == 0) { + } else if (rs == 0) { gpr.SetImm(rt, simm); } else { gpr.SpillLock(rs, rt); @@ -83,12 +84,12 @@ namespace MIPSComp ADD(gpr.R(rt), gpr.R(rs), R0); } gpr.ReleaseSpillLocks(); - }*/ + } else { Comp_Generic(op); } break; - } + } */ /* case 13: // OR { From dafe2c389cf7dfa49584f8869bfa026d9382050c Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 13:38:44 +0100 Subject: [PATCH 22/49] More regalloc fixing and tweaks. Still not working the way I want it. --- Common/ArmEmitter.cpp | 13 +++++-- Common/ArmEmitter.h | 6 +++- Core/MIPS/ARM/ArmAsm.cpp | 2 -- Core/MIPS/ARM/ArmCompALU.cpp | 64 +++++++++++---------------------- Core/MIPS/ARM/ArmCompBranch.cpp | 16 ++++----- Core/MIPS/ARM/ArmJit.cpp | 1 + Core/MIPS/ARM/ArmRegCache.cpp | 35 ++++++++++++------ Core/MIPS/ARM/ArmRegCache.h | 7 +++- 8 files changed, 77 insertions(+), 67 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index e4d18b6921..8b17e912c9 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -54,8 +54,6 @@ bool TryMakeOperand2(u32 imm, Operand2 &op2) { bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse) { if (!TryMakeOperand2(imm, op2)) { - return false; - // TODO: Test properly. *inverse = true; return TryMakeOperand2(~imm, op2); } else { @@ -64,6 +62,17 @@ bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse) } } +bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) +{ + if (!TryMakeOperand2(imm, op2)) { + *negated = true; + return TryMakeOperand2(-imm, op2); + } else { + *negated = false; + return true; + } +} + void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 45da845648..a7d15406b9 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -298,9 +298,13 @@ public: }; - +// Use these when you don't know if an imm can be represented as an operand2. +// This lets you generate both an optimal and a fallback solution by checking +// the return value, which will be false if these fail to find a Operand2 that +// represents your 32-bit imm value. bool TryMakeOperand2(u32 imm, Operand2 &op2); bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse); +bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated); inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); } diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 34604e497e..52e7376a3f 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -80,10 +80,8 @@ void DisassembleArm(const u8 *data, int size); // At this offset - 4, there is an int specifying the block number. void ArmAsmRoutineManager::QuickCallFunction(ARMReg reg, void *func) { - PUSH(1, _LR); ARMABI_MOVI2R(reg, (u32)(func)); BL(reg); - POP(1, _LR); } void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 36ea208cd4..c9c5c743e1 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -34,10 +34,9 @@ using namespace MIPSAnalyst; namespace MIPSComp { - u32 EvalOR(u32 a, u32 b) { return a | b; } - u32 EvalXOR(u32 a, u32 b) { return a ^ b; } - u32 EvalAND(u32 a, u32 b) { return a & b; } - + static u32 EvalOR(u32 a, u32 b) { return a | b; } + static u32 EvalXOR(u32 a, u32 b) { return a ^ b; } + static u32 EvalAND(u32 a, u32 b) { return a & b; } void Jit::CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)) { @@ -45,8 +44,8 @@ namespace MIPSComp gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm)); } else { gpr.SpillLock(rs, rt); - gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); - gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rt, MAP_DIRTY); + gpr.MapReg(rs); gpr.ReleaseSpillLocks(); // TODO: Special case when uimm can be represented as an Operand2 ARMABI_MOVI2R(R0, (u32)uimm); @@ -56,7 +55,7 @@ namespace MIPSComp void Jit::Comp_IType(u32 op) { - s32 simm = (s16)(op & 0xFFFF); + s32 simm = (s16)(op & 0xFFFF); // sign extension u32 uimm = (u16)(op & 0xFFFF); int rt = _RT; @@ -64,18 +63,18 @@ namespace MIPSComp switch (op >> 26) { - /* case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { if (gpr.IsImm(rs)) { gpr.SetImm(rt, gpr.GetImm(rs) + simm); - } else if (rs == 0) { - gpr.SetImm(rt, simm); + } else if (rs == 0) { // add to zero register = immediate + gpr.SetImm(rt, (u32)simm); } else { gpr.SpillLock(rs, rt); - gpr.MapReg(rs, MAP_INITVAL); - gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); + gpr.MapReg(rs); + gpr.MapReg(rt, MAP_DIRTY); + gpr.ReleaseSpillLocks(); Operand2 op2; if (false && TryMakeOperand2(simm, op2)) { ADD(gpr.R(rt), gpr.R(rs), op2); @@ -83,36 +82,15 @@ namespace MIPSComp ARMABI_MOVI2R(R0, (u32)simm); ADD(gpr.R(rt), gpr.R(rs), R0); } - gpr.ReleaseSpillLocks(); - } - else { + } /* else { Comp_Generic(op); - } - break; - } */ -/* - case 13: // OR - { - if (gpr.IsImm(rs)) - { - gpr.SetImm(rt, gpr.GetImm(rs) | uimm); - break; - } else if (rs == 0) { - gpr.SetImm(rt, uimm); - } else { - gpr.SpillLock(rs, rt); - gpr.MapReg(rs, MAP_INITVAL); - gpr.MapReg(rt, MAP_INITVAL | MAP_DIRTY); - ARMABI_MOVI2R(R0, (u32)uimm); - ORR(gpr.R(rt), gpr.R(rs), R0); - gpr.ReleaseSpillLocks(); - } + } */ break; } - break; - */ - //case 12: CompImmLogic(op, &XEmitter::AND); break; - //case 14: CompImmLogic(op, &XEmitter::XOR); break; + + //case 12: CompImmLogic(op, &XEmitter::AND, EvalAnd); break; + //case 13: CompImmLogic(op, &XEmitter::OR, EvalOr); break; + //case 14: CompImmLogic(op, &XEmitter::XOR, EvalXor); break; /* case 10: // R(rt) = (s32)R(rs) < simm; break; //slti @@ -137,7 +115,7 @@ namespace MIPSComp */ - case 15: //R(rt) = uimm << 16; break; //lui + case 15: // R(rt) = uimm << 16; //lui gpr.SetImm(rt, uimm << 16); break; @@ -174,9 +152,9 @@ namespace MIPSComp int rd = _RD; gpr.SpillLock(rt, rs, rd); - gpr.MapReg(rt, MAP_INITVAL); - gpr.MapReg(rs, MAP_INITVAL); - gpr.MapReg(rd, MAP_INITVAL | MAP_DIRTY); // can get rid of INITVAL in some cases + gpr.MapReg(rt); + gpr.MapReg(rs); + gpr.MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases gpr.ReleaseSpillLocks(); switch (op & 63) diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 7a4deaed8d..20d1fa18b2 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -65,19 +65,19 @@ void Jit::BranchRSRTComp(u32 op, ArmGen::CCFlags cc, bool likely) if (rt == 0) { - gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rs); CMP(gpr.R(rs), Operand2(0, TYPE_IMM)); } else if (rs == 0 && (cc == CC_EQ || cc == CC_NEQ)) // only these are easily 'flippable' { - gpr.MapReg(rt, MAP_INITVAL); - CMP(gpr.R(rt), Operand2(0)); + gpr.MapReg(rt); + CMP(gpr.R(rt), Operand2(0, TYPE_IMM)); } else { gpr.SpillLock(rs, rt); - gpr.MapReg(rs, MAP_INITVAL); - gpr.MapReg(rt, MAP_INITVAL); + gpr.MapReg(rs); + gpr.MapReg(rt); gpr.ReleaseSpillLocks(); CMP(gpr.R(rs), gpr.R(rt)); } @@ -133,7 +133,7 @@ void Jit::BranchRSZeroComp(u32 op, ArmGen::CCFlags cc, bool likely) { // ERROR_LOG(CPU, "Not nice delay slot in BranchRSZeroComp :( %08x", js.compilerPC); } - gpr.MapReg(rs, MAP_INITVAL); + gpr.MapReg(rs); CMP(gpr.R(rs), Operand2(0, TYPE_IMM)); FlushAll(); @@ -383,15 +383,15 @@ void Jit::Comp_JumpReg(u32 op) // Do what with that information? delaySlotIsNice = false; - gpr.MapReg(rs, MAP_INITVAL); - if (delaySlotIsNice) { CompileAt(js.compilerPC + 4); + gpr.MapReg(rs); MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented FlushAll(); MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking. } else { // Delay slot + gpr.MapReg(rs); MOV(R8, gpr.R(rs)); // Save the destination address through the delay slot. Could use isNice to avoid when the jit is fully implemented MovToPC(R8); // For syscall to be able to return. Could be avoided with some checking. CompileAt(js.compilerPC + 4); diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 7ff148aa1c..a34be300c3 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -137,6 +137,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) #endif while (js.compiling) { + gpr.SetCompilerPC(js.compilerPC); // Let it know for log messages u32 inst = Memory::Read_Instruction(js.compilerPC); #ifdef LOGASM if (logBlocks > 0) { diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 7c34066b83..090feddf78 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -58,11 +58,11 @@ static const ARMReg *GetMIPSAllocationOrder(int &count) { ARMReg ArmRegCache::MapReg(MIPSReg mipsReg, int mapFlags) { // Let's see if it's already mapped. If so we just need to update the dirty flag. - // We don't need to check for ML_INITVAL because we assume that anyone who maps + // We don't need to check for ML_NOINIT because we assume that anyone who maps // with that flag immediately writes a "known" value to the register. if (mr[mipsReg].loc == ML_ARMREG) { if (ar[mr[mipsReg].reg].mipsReg != mipsReg) { - ERROR_LOG(HLE, "Mapping out of sync! %i", mipsReg); + ERROR_LOG(HLE, "Register mapping out of sync! %i", mipsReg); } if (mapFlags & MAP_DIRTY) { ar[mr[mipsReg].reg].isDirty = true; @@ -83,7 +83,7 @@ allocate: // That means it's free. Grab it, and load the value into it (if requested). ar[reg].mipsReg = mipsReg; ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; - if (mapFlags & MAP_INITVAL) { + if (!(mapFlags & MAP_NOINIT)) { if (mr[mipsReg].loc == ML_MEM) emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); else if (mr[mipsReg].loc == ML_IMM) @@ -95,17 +95,25 @@ allocate: } } - // Still nothing. Let's spill a reg and goto 10 - + // Still nothing. Let's spill a reg and goto 10. + // TODO: Use age or something to choose which register to spill? + int bestToSpill = -1; for (int i = 0; i < allocCount; i++) { - if (ar[i].spillLock || ar[i].allocLock) + int reg = allocOrder[i]; + if (ar[reg].spillLock || ar[reg].allocLock) continue; - FlushArmReg((ARMReg)i); + bestToSpill = reg; + break; + } + + if (bestToSpill != -1) { + WARN_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill); + FlushArmReg((ARMReg)bestToSpill); goto allocate; } // Uh oh, we have all them alloclocked and spilllocked.... - _assert_msg_(JIT, false, "All available registers are locked dumb dumb"); + ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc); return INVALID_REG; } @@ -146,12 +154,20 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { break; } mr[r].loc = ML_MEM; + mr[r].reg = INVALID_REG; + mr[r].imm = 0; } void ArmRegCache::FlushAll() { for (int i = 0; i < NUM_MIPSREG; i++) { FlushMipsReg(i); } + // Sanity check + for (int i = 0; i < NUM_ARMREG; i++) { + if (ar[i].mipsReg != -1) { + ERROR_LOG(JIT, "Flush fail: ar[%i].mipsReg=%i", i, ar[i].mipsReg); + } + } } void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { @@ -204,8 +220,7 @@ ARMReg ArmRegCache::R(int mipsReg) { return mr[mipsReg].reg; } else { _dbg_assert_msg_(JIT, false, "R: not mapped"); - ERROR_LOG(JIT, "Reg %i not in arm reg", mipsReg); + ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_); return INVALID_REG; // BAAAD } } - diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 73e6c50642..cfc490a87d 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -60,9 +60,10 @@ struct RegMIPS { // If loc == ML_MEM, it's back in its location in the CPU context struct. }; +// Initing is the default so the flag is reversed. enum { MAP_DIRTY = 1, - MAP_INITVAL = 2, + MAP_NOINIT = 2, }; class ArmRegCache @@ -97,10 +98,14 @@ public: void SetEmitter(ARMXEmitter *emitter) { emit = emitter; } + // For better log output only. + void SetCompilerPC(u32 compilerPC) { compilerPC_ = compilerPC; } + private: int GetMipsRegOffset(MIPSReg r); MIPSState *mips_; ARMXEmitter *emit; + u32 compilerPC_; enum { NUM_ARMREG = 16, From 2f4e6eaf0135ced03e9d28f76e7c50004effa0ab Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 22:46:27 +0100 Subject: [PATCH 23/49] Remove allocLock until we need it --- Core/MIPS/ARM/ArmCompALU.cpp | 3 ++- Core/MIPS/ARM/ArmRegCache.cpp | 7 +++---- Core/MIPS/ARM/ArmRegCache.h | 4 ---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index c9c5c743e1..fa83ae0d0c 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -63,6 +63,7 @@ namespace MIPSComp switch (op >> 26) { +#if 0 case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { @@ -87,7 +88,7 @@ namespace MIPSComp } */ break; } - +#endif //case 12: CompImmLogic(op, &XEmitter::AND, EvalAnd); break; //case 13: CompImmLogic(op, &XEmitter::OR, EvalOr); break; //case 14: CompImmLogic(op, &XEmitter::XOR, EvalXor); break; diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 090feddf78..20a16ce5d8 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -34,7 +34,6 @@ void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { for (int i = 0; i < 16; i++) { ar[i].mipsReg = -1; ar[i].spillLock = false; - ar[i].allocLock = false; ar[i].isDirty = false; } for (int i = 0; i < NUM_MIPSREG; i++) { @@ -79,7 +78,7 @@ allocate: for (int i = 0; i < allocCount; i++) { int reg = allocOrder[i]; - if (ar[reg].mipsReg == -1 && !ar[reg].allocLock) { + if (ar[reg].mipsReg == -1) { // That means it's free. Grab it, and load the value into it (if requested). ar[reg].mipsReg = mipsReg; ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; @@ -100,7 +99,7 @@ allocate: int bestToSpill = -1; for (int i = 0; i < allocCount; i++) { int reg = allocOrder[i]; - if (ar[reg].spillLock || ar[reg].allocLock) + if (ar[reg].spillLock) continue; bestToSpill = reg; break; @@ -112,7 +111,7 @@ allocate: goto allocate; } - // Uh oh, we have all them alloclocked and spilllocked.... + // Uh oh, we have all them spilllocked.... ERROR_LOG(JIT, "Out of spillable registers at PC %08x!!!", mips_->pc); return INVALID_REG; } diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index cfc490a87d..127396ddc4 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -41,7 +41,6 @@ typedef int MIPSReg; struct RegARM { int mipsReg; // if -1, no mipsreg attached. bool isDirty; // Should the register be written back? - bool allocLock; // if true, this ARM register cannot be used for allocation. bool spillLock; // if true, this register cannot be spilled. }; @@ -75,9 +74,6 @@ public: void Init(ARMXEmitter *emitter); void Start(MIPSAnalyst::AnalysisResults &stats); - // void AllocLock(ARMReg reg, ARMReg reg2 = INVALID_REG, ARMReg reg3 = INVALID_REG); - // void ReleaseAllocLock(ARMReg reg); - // Protect the arm register containing a MIPS register from spilling, to ensure that // it's being kept allocated. void SpillLock(MIPSReg reg, MIPSReg reg2 = -1, MIPSReg reg3 = -1); From fb7116ccd5bf56458f6fbe295e2ef3e71cea926b Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 22:55:58 +0100 Subject: [PATCH 24/49] Add more checks. --- Core/MIPS/ARM/ArmJit.cpp | 4 ++-- Core/MIPS/ARM/ArmRegCache.cpp | 21 +++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index a34be300c3..54cda87a45 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -128,7 +128,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) int numInstructions = 0; int cycles = 0; - static int logBlocks = 20; + static int logBlocks = 1; if (logBlocks > 0) logBlocks--; #define LOGASM @@ -179,7 +179,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) void Jit::Comp_RunBlock(u32 op) { // This shouldn't be necessary, the dispatcher should catch us before we get here. - ERROR_LOG(DYNA_REC, "Comp_RunBlock"); + ERROR_LOG(DYNA_REC, "Comp_RunBlock should never be reached!"); } void Jit::Comp_Generic(u32 op) diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 20a16ce5d8..cd1a05400e 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -94,6 +94,8 @@ allocate: } } + ERROR_LOG(JIT, "Spill!"); + // Still nothing. Let's spill a reg and goto 10. // TODO: Use age or something to choose which register to spill? int bestToSpill = -1; @@ -106,7 +108,7 @@ allocate: } if (bestToSpill != -1) { - WARN_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill); + ERROR_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill); FlushArmReg((ARMReg)bestToSpill); goto allocate; } @@ -118,7 +120,7 @@ allocate: void ArmRegCache::FlushArmReg(ARMReg r) { if (ar[r].mipsReg == -1) { - // Nothing to do + // Nothing to do, reg not mapped. return; } if (ar[r].mipsReg != -1) { @@ -126,6 +128,8 @@ void ArmRegCache::FlushArmReg(ARMReg r) { emit->STR(CTXREG, r, 4 * ar[r].mipsReg); // IMMs won't be in an ARM reg. mr[ar[r].mipsReg].loc = ML_MEM; + mr[ar[r].mipsReg].reg = INVALID_REG; + mr[ar[r].mipsReg].imm = 0; } else { ERROR_LOG(HLE, "Dirty but no mipsreg?"); } @@ -142,10 +146,14 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { break; case ML_ARMREG: - if (ar[mr[r].reg].isDirty) + if (mr[r].reg == INVALID_REG) { + ERROR_LOG(HLE, "FlushMipsReg: MipsReg had bad ArmReg"); + } + if (ar[mr[r].reg].isDirty) { emit->STR(CTXREG, mr[r].reg, GetMipsRegOffset(r)); + ar[mr[r].reg].isDirty = false; + } ar[mr[r].reg].mipsReg = -1; - ar[mr[r].reg].isDirty = false; break; default: @@ -171,8 +179,10 @@ void ArmRegCache::FlushAll() { void ArmRegCache::SetImm(MIPSReg r, u32 immVal) { // Zap existing value if cached in a reg - if (mr[r].loc == ML_ARMREG) + if (mr[r].loc == ML_ARMREG) { ar[mr[r].reg].mipsReg = -1; + ar[mr[r].reg].isDirty = false; + } mr[r].loc = ML_IMM; mr[r].imm = immVal; mr[r].reg = INVALID_REG; @@ -218,7 +228,6 @@ ARMReg ArmRegCache::R(int mipsReg) { if (mr[mipsReg].loc == ML_ARMREG) { return mr[mipsReg].reg; } else { - _dbg_assert_msg_(JIT, false, "R: not mapped"); ERROR_LOG(JIT, "Reg %i not in arm reg. compilerPC = %08x", mipsReg, compilerPC_); return INVALID_REG; // BAAAD } From 209f1d79a9ce983cd0da4471fe04176ab557733b Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Wed, 9 Jan 2013 23:14:21 +0100 Subject: [PATCH 25/49] SpillLock is now per MIPS register instead of per ARM register. Fix array size. --- Core/MIPS/ARM/ArmRegCache.cpp | 14 +++++++------- Core/MIPS/ARM/ArmRegCache.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index cd1a05400e..5a2487204f 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -33,13 +33,13 @@ void ArmRegCache::Init(ARMXEmitter *emitter) { void ArmRegCache::Start(MIPSAnalyst::AnalysisResults &stats) { for (int i = 0; i < 16; i++) { ar[i].mipsReg = -1; - ar[i].spillLock = false; ar[i].isDirty = false; } for (int i = 0; i < NUM_MIPSREG; i++) { mr[i].loc = ML_MEM; mr[i].reg = INVALID_REG; mr[i].imm = -1; + mr[i].spillLock = false; } } @@ -101,7 +101,7 @@ allocate: int bestToSpill = -1; for (int i = 0; i < allocCount; i++) { int reg = allocOrder[i]; - if (ar[reg].spillLock) + if (ar[reg].mipsReg != -1 && mr[ar[reg].mipsReg].spillLock) continue; bestToSpill = reg; break; @@ -213,14 +213,14 @@ int ArmRegCache::GetMipsRegOffset(MIPSReg r) { } void ArmRegCache::SpillLock(MIPSReg r1, MIPSReg r2, MIPSReg r3) { - if (mr[r1].loc == ML_ARMREG) ar[mr[r1].reg].spillLock = true; - if (r2 != -1 && mr[r2].loc == ML_ARMREG) ar[mr[r2].reg].spillLock = true; - if (r3 != -1 && mr[r3].loc == ML_ARMREG) ar[mr[r3].reg].spillLock = true; + mr[r1].spillLock = true; + if (r2 != -1) mr[r2].spillLock = true; + if (r3 != -1) mr[r3].spillLock = true; } void ArmRegCache::ReleaseSpillLocks() { - for (int i = 0; i < NUM_ARMREG; i++) { - ar[i].spillLock = false; + for (int i = 0; i < NUM_MIPSREG; i++) { + mr[i].spillLock = false; } } diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 127396ddc4..8154c93c8f 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -41,7 +41,6 @@ typedef int MIPSReg; struct RegARM { int mipsReg; // if -1, no mipsreg attached. bool isDirty; // Should the register be written back? - bool spillLock; // if true, this register cannot be spilled. }; enum RegMIPSLoc { @@ -56,6 +55,7 @@ struct RegMIPS { // Data (only one of these is used, depending on loc. Could make a union). u32 imm; ARMReg reg; + bool spillLock; // if true, this register cannot be spilled. // If loc == ML_MEM, it's back in its location in the CPU context struct. }; @@ -108,6 +108,6 @@ private: NUM_MIPSREG = TOTAL_MAPPABLE_MIPSREGS, }; - RegARM ar[16]; - RegMIPS mr[32]; + RegARM ar[NUM_ARMREG]; + RegMIPS mr[NUM_MIPSREG]; }; From 71652874c2f82aff963b96338bd65081af3c13f4 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 00:03:51 +0100 Subject: [PATCH 26/49] Fix jit bugs related to the dirty flag, and more: * Tweak block logging * Faster calls-to-interpreter. --- Common/ArmEmitter.cpp | 5 +++++ Common/ArmEmitter.h | 1 + Core/MIPS/ARM/ArmAsm.cpp | 4 ---- Core/MIPS/ARM/ArmAsm.h | 1 - Core/MIPS/ARM/ArmCompALU.cpp | 12 ++++-------- Core/MIPS/ARM/ArmJit.cpp | 16 +++++++++------- Core/MIPS/ARM/ArmRegCache.cpp | 10 +++++----- 7 files changed, 24 insertions(+), 25 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 8b17e912c9..b4539c0bdd 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -73,6 +73,11 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } +void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { + ARMABI_MOVI2R(reg, (u32)(func)); + BL(reg); +} + void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index a7d15406b9..459c4231f4 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -471,6 +471,7 @@ public: void VLDR(ARMReg dest, ARMReg Base, Operand2 op); void VMOV(ARMReg Dest, ARMReg Src); + void QuickCallFunction(ARMReg scratchreg, void *func); // Utility functions void ARMABI_CallFunction(void *func); void ARMABI_CallFunctionC(void *func, u32 Arg0); diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 52e7376a3f..63c5cd52a8 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -79,10 +79,6 @@ void DisassembleArm(const u8 *data, int size); // dynarec buffer // At this offset - 4, there is an int specifying the block number. -void ArmAsmRoutineManager::QuickCallFunction(ARMReg reg, void *func) { - ARMABI_MOVI2R(reg, (u32)(func)); - BL(reg); -} void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) { diff --git a/Core/MIPS/ARM/ArmAsm.h b/Core/MIPS/ARM/ArmAsm.h index 14af4e2dcd..3e15919df1 100644 --- a/Core/MIPS/ARM/ArmAsm.h +++ b/Core/MIPS/ARM/ArmAsm.h @@ -57,7 +57,6 @@ public: private: void Generate(MIPSState *mips, MIPSComp::Jit *jit); - void QuickCallFunction(ArmGen::ARMReg reg, void *func); }; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index fa83ae0d0c..0099195b3b 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -55,7 +55,7 @@ namespace MIPSComp void Jit::Comp_IType(u32 op) { - s32 simm = (s16)(op & 0xFFFF); // sign extension + s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension u32 uimm = (u16)(op & 0xFFFF); int rt = _RT; @@ -63,7 +63,6 @@ namespace MIPSComp switch (op >> 26) { -#if 0 case 8: // same as addiu? case 9: //R(rt) = R(rs) + simm; break; //addiu { @@ -73,22 +72,19 @@ namespace MIPSComp gpr.SetImm(rt, (u32)simm); } else { gpr.SpillLock(rs, rt); - gpr.MapReg(rs); gpr.MapReg(rt, MAP_DIRTY); + gpr.MapReg(rs); gpr.ReleaseSpillLocks(); Operand2 op2; - if (false && TryMakeOperand2(simm, op2)) { + if (TryMakeOperand2(simm, op2)) { ADD(gpr.R(rt), gpr.R(rs), op2); } else { ARMABI_MOVI2R(R0, (u32)simm); ADD(gpr.R(rt), gpr.R(rs), R0); } - } /* else { - Comp_Generic(op); - } */ + } break; } -#endif //case 12: CompImmLogic(op, &XEmitter::AND, EvalAnd); break; //case 13: CompImmLogic(op, &XEmitter::OR, EvalOr); break; //case 14: CompImmLogic(op, &XEmitter::XOR, EvalXor); break; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 54cda87a45..9931492abd 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -127,9 +127,10 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) int numInstructions = 0; int cycles = 0; - - static int logBlocks = 1; + static int dontLogBlocks = 20; + static int logBlocks = 40; if (logBlocks > 0) logBlocks--; + if (dontLogBlocks > 0) dontLogBlocks--; #define LOGASM #ifdef LOGASM @@ -140,7 +141,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) gpr.SetCompilerPC(js.compilerPC); // Let it know for log messages u32 inst = Memory::Read_Instruction(js.compilerPC); #ifdef LOGASM - if (logBlocks > 0) { + if (logBlocks > 0 && dontLogBlocks == 0) { MIPSDisAsm(inst, js.compilerPC, temp, true); INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); } @@ -148,12 +149,12 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) js.downcountAmount += MIPSGetInstructionCycleEstimate(inst); MIPSCompileOp(inst); - + // FlushAll(); ///HACKK js.compilerPC += 4; numInstructions++; } #ifdef LOGASM - if (logBlocks > 0) { + if (logBlocks > 0 && dontLogBlocks == 0) { MIPSDisAsm(Memory::Read_Instruction(js.compilerPC), js.compilerPC, temp, true); INFO_LOG(DYNA_REC, "M: %08x %s", js.compilerPC, temp); } @@ -162,7 +163,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) b->codeSize = GetCodePtr() - b->normalEntry; #ifdef LOGASM - if (logBlocks > 0) { + if (logBlocks > 0 && dontLogBlocks == 0) { INFO_LOG(DYNA_REC, "=============== ARM ==============="); DisassembleArm(b->normalEntry, GetCodePtr() - b->normalEntry); } @@ -190,7 +191,8 @@ void Jit::Comp_Generic(u32 op) { ARMABI_MOVI2R(R0, js.compilerPC); MovToPC(R0); - ARMABI_CallFunctionC((void *)func, op); + ARMABI_MOVI2R(R0, op); + QuickCallFunction(R1, (void *)func); } } diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 5a2487204f..169cc601a0 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -80,22 +80,22 @@ allocate: if (ar[reg].mipsReg == -1) { // That means it's free. Grab it, and load the value into it (if requested). - ar[reg].mipsReg = mipsReg; ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; if (!(mapFlags & MAP_NOINIT)) { - if (mr[mipsReg].loc == ML_MEM) + if (mr[mipsReg].loc == ML_MEM) { emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); - else if (mr[mipsReg].loc == ML_IMM) + } else if (mr[mipsReg].loc == ML_IMM) { emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm); + ar[reg].isDirty = true; + } } + ar[reg].mipsReg = mipsReg; mr[mipsReg].loc = ML_ARMREG; mr[mipsReg].reg = (ARMReg)reg; return (ARMReg)reg; } } - ERROR_LOG(JIT, "Spill!"); - // Still nothing. Let's spill a reg and goto 10. // TODO: Use age or something to choose which register to spill? int bestToSpill = -1; From 9dcdb6df9001fdeeb71d325d532def5adfadae09 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 01:08:24 +0100 Subject: [PATCH 27/49] Jit work! Shifts, 3-operand ops, turn O2 back on... --- Core/MIPS/ARM/ArmCompALU.cpp | 111 +++++++++++++++++++++------------- Core/MIPS/ARM/ArmJit.h | 3 +- Core/MIPS/ARM/ArmRegCache.cpp | 6 +- android/jni/Android.mk | 2 +- 4 files changed, 76 insertions(+), 46 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 0099195b3b..c50c0e497b 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -34,9 +34,9 @@ using namespace MIPSAnalyst; namespace MIPSComp { - static u32 EvalOR(u32 a, u32 b) { return a | b; } - static u32 EvalXOR(u32 a, u32 b) { return a ^ b; } - static u32 EvalAND(u32 a, u32 b) { return a & b; } + static u32 EvalOr(u32 a, u32 b) { return a | b; } + static u32 EvalXor(u32 a, u32 b) { return a ^ b; } + static u32 EvalAnd(u32 a, u32 b) { return a & b; } void Jit::CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)) { @@ -48,8 +48,13 @@ namespace MIPSComp gpr.MapReg(rs); gpr.ReleaseSpillLocks(); // TODO: Special case when uimm can be represented as an Operand2 - ARMABI_MOVI2R(R0, (u32)uimm); - (this->*arith)(gpr.R(rt), gpr.R(rs), R0); + Operand2 op2; + if (TryMakeOperand2(uimm, op2)) { + (this->*arith)(gpr.R(rt), gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)uimm); + (this->*arith)(gpr.R(rt), gpr.R(rs), R0); + } } } @@ -64,7 +69,7 @@ namespace MIPSComp switch (op >> 26) { case 8: // same as addiu? - case 9: //R(rt) = R(rs) + simm; break; //addiu + case 9: // R(rt) = R(rs) + simm; break; //addiu { if (gpr.IsImm(rs)) { gpr.SetImm(rt, gpr.GetImm(rs) + simm); @@ -85,32 +90,56 @@ namespace MIPSComp } break; } - //case 12: CompImmLogic(op, &XEmitter::AND, EvalAnd); break; - //case 13: CompImmLogic(op, &XEmitter::OR, EvalOr); break; - //case 14: CompImmLogic(op, &XEmitter::XOR, EvalXor); break; - /* + case 12: CompImmLogic(rs, rt, uimm, &ARMXEmitter::AND, &EvalAnd); break; + case 13: CompImmLogic(rs, rt, uimm, &ARMXEmitter::ORR, &EvalOr); break; + case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &EvalXor); break; + case 10: // R(rt) = (s32)R(rs) < simm; break; //slti - gpr.Lock(rt, rs); - gpr.BindToRegister(rt, rt == rs, true); - XOR(32, R(EAX), R(EAX)); - CMP(32, gpr.R(rs), Imm32(simm)); - SETcc(CC_L, R(EAX)); - MOV(32, gpr.R(rt), R(EAX)); - gpr.UnlockAll(); - break; + gpr.SpillLock(rt, rs); + gpr.MapReg(rs); + gpr.MapReg(rt, MAP_DIRTY); + gpr.ReleaseSpillLocks(); + { + Operand2 op2; + if (TryMakeOperand2(simm, op2)) { + CMP(gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, simm); + CMP(gpr.R(rs), R0); + } + } + + SetCC(CC_LT); + ARMABI_MOVI2R(gpr.R(rt), 1); + SetCC(CC_GE); + ARMABI_MOVI2R(gpr.R(rt), 0); + SetCC(CC_AL); + break; + case 11: // R(rt) = R(rs) < uimm; break; //sltiu - gpr.Lock(rt, rs); - gpr.BindToRegister(rt, rt == rs, true); - XOR(32, R(EAX), R(EAX)); - CMP(32, gpr.R(rs), Imm32((u32)simm)); - SETcc(CC_B, R(EAX)); - MOV(32, gpr.R(rt), R(EAX)); - gpr.UnlockAll(); - break; + gpr.SpillLock(rt, rs); + gpr.MapReg(rs); + gpr.MapReg(rt, MAP_DIRTY); + gpr.ReleaseSpillLocks(); - */ + { + Operand2 op2; + if (TryMakeOperand2(uimm, op2)) { + CMP(gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, uimm); + CMP(gpr.R(rs), R0); + } + } + + SetCC(CC_LO); + ARMABI_MOVI2R(gpr.R(rt), 1); + SetCC(CC_HS); + ARMABI_MOVI2R(gpr.R(rt), 0); + SetCC(CC_AL); + break; case 15: // R(rt) = uimm << 16; //lui gpr.SetImm(rt, uimm << 16); @@ -142,8 +171,6 @@ namespace MIPSComp void Jit::Comp_RType3(u32 op) { - OLDD - int rt = _RT; int rs = _RS; int rd = _RD; @@ -213,21 +240,20 @@ namespace MIPSComp } } - /* - - void Jit::CompShiftImm(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)) + void Jit::CompShiftImm(u32 op, ArmGen::ShiftType shiftType) { int rd = _RD; int rt = _RT; - gpr.Lock(rd, rt); int sa = _SA; - gpr.BindToRegister(rd, rd == rt, true); - if (rd != rt) - MOV(32, gpr.R(rd), gpr.R(rt)); - (this->*shift)(32, gpr.R(rd), Imm8(sa)); - gpr.UnlockAll(); + + gpr.SpillLock(rd, rt); + gpr.MapReg(rt); + gpr.MapReg(rd, MAP_DIRTY); + gpr.ReleaseSpillLocks(); + + MOV(gpr.R(rd), Operand2(sa, shiftType, gpr.R(rt))); } - */ + // "over-shifts" work the same as on x86 - only bottom 5 bits are used to get the shift value /* void Jit::CompShiftVar(u32 op, void (XEmitter::*shift)(int, OpArg, OpArg)) @@ -250,12 +276,11 @@ namespace MIPSComp void Jit::Comp_ShiftType(u32 op) { // WARNIGN : ROTR - OLDD switch (op & 0x3f) { - //case 0: CompShiftImm(op, &ARMXEmitter::SHL); break; - //case 2: CompShiftImm(op, &XEmitter::SHR); break; // srl - //case 3: CompShiftImm(op, &XEmitter::SAR); break; // sra + case 0: CompShiftImm(op, ST_LSL); break; + case 2: CompShiftImm(op, ST_LSR); break; // srl + case 3: CompShiftImm(op, ST_ASR); break; // sra // case 4: CompShiftVar(op, &XEmitter::SHL); break; // R(rd) = R(rt) << R(rs); break; //sllv // case 6: CompShiftVar(op, &XEmitter::SHR); break; // R(rd) = R(rt) >> R(rs); break; //srlv diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 805f44da1b..3fefdf68a7 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -111,7 +111,8 @@ private: // Utilities to reduce duplicated code void CompImmLogic(int rs, int rt, u32 uimm, void (ARMXEmitter::*arith)(ARMReg dst, ARMReg src, Operand2 op2), u32 (*eval)(u32 a, u32 b)); - /* + void CompShiftImm(u32 op, ArmGen::ShiftType shiftType); + /* void CompImmLogic(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompTriArith(u32 op, void (ARMXEmitter::*arith)(int, const OpArg &, const OpArg &)); void CompShiftImm(u32 op, void (ARMXEmitter::*shift)(int, OpArg, OpArg)); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 169cc601a0..10155d9c7d 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -86,7 +86,7 @@ allocate: emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); } else if (mr[mipsReg].loc == ML_IMM) { emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm); - ar[reg].isDirty = true; + ar[reg].isDirty = true; // IMM is always dirty. } } ar[reg].mipsReg = mipsReg; @@ -156,6 +156,10 @@ void ArmRegCache::FlushMipsReg(MIPSReg r) { ar[mr[r].reg].mipsReg = -1; break; + case ML_MEM: + // Already there, nothing to do. + break; + default: //BAD break; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 7155dce860..90c0e9827c 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -24,7 +24,7 @@ LOCAL_MODULE := ppsspp_jni NATIVE := ../../native SRC := ../.. -LOCAL_CFLAGS := -DUSE_PROFILER -DARM -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -g -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math +LOCAL_CFLAGS := -DUSE_PROFILER -DARM -DGL_GLEXT_PROTOTYPES -DUSING_GLES2 -O2 -fsigned-char -Wall -Wno-multichar -Wno-psabi -Wno-unused-variable -fno-strict-aliasing -ffast-math LOCAL_CXXFLAGS := -std=gnu++0x LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/../../Common \ From 8cd5ae933f4bacd8d828cb9042af4c7bc9f9abf8 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 01:38:15 +0100 Subject: [PATCH 28/49] sw/lw --- Core/MIPS/ARM/ArmCompLoadStore.cpp | 64 ++++++++++++++++++++---------- Core/MIPS/ARM/ArmRegCache.cpp | 3 +- 2 files changed, 45 insertions(+), 22 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index fd458240f0..524c6fdb7d 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -16,7 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "../../MemMap.h" #include "../MIPSAnalyst.h" - +#include "../../Config.h" #include "ArmJit.h" #include "ArmRegCache.h" @@ -36,8 +36,6 @@ namespace MIPSComp { void Jit::Comp_ITypeMem(u32 op) { - OLDD - int offset = (signed short)(op&0xFFFF); int rt = _RT; int rs = _RS; @@ -54,13 +52,26 @@ namespace MIPSComp return; case 35: //R(rt) = ReadMem32(addr); break; //lw - /* - gpr.Lock(rt, rs); - gpr.BindToRegister(rt, rt == rs, true); - MOV(32, R(EAX), gpr.R(rs)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, gpr.R(rt), MDisp(EAX, (u32)Memory::base + offset)); - gpr.UnlockAll();*/ + if (true || g_Config.bFastMemory) { + gpr.SpillLock(rt, rs); + gpr.MapReg(rt, MAP_DIRTY); + gpr.MapReg(rs); + gpr.ReleaseSpillLocks(); + + Operand2 op2; + if (TryMakeOperand2(offset, op2)) { + ADD(R0, gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + ADD(R0, R0, R11); + LDR(gpr.R(rt), R0); + } else { + Comp_Generic(op); + return; + } break; case 132: //R(rt) = (u32)(s32)(s8) ReadMem8 (addr); break; //lb @@ -74,18 +85,29 @@ namespace MIPSComp return; case 43: //WriteMem32(addr, R(rt)); break; //sw - /* - { - gpr.Lock(rt, rs); - gpr.BindToRegister(rt, true, false); - MOV(32, R(EAX), gpr.R(rs)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, MDisp(EAX, (u32)Memory::base + offset), gpr.R(rt)); - gpr.UnlockAll(); - }*/ + if (true || g_Config.bFastMemory) { + gpr.SpillLock(rt, rs); + gpr.MapReg(rt); + gpr.MapReg(rs); + gpr.ReleaseSpillLocks(); + Operand2 op2; + if (TryMakeOperand2(offset, op2)) { + ADD(R0, gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + ADD(R0, R0, R11); + STR(R0, gpr.R(rt)); + } else { + Comp_Generic(op); + return; + } break; - + // break; + /* case 134: //lwl { Crash(); @@ -123,7 +145,7 @@ namespace MIPSComp // WriteMem32((addr & 0xfffffffc), ( ( u32(R(rt)) << shift ) | // (mem & (0x00ffffff >> (24 - shift)) ) ) ); } - break; + break;*/ default: Comp_Generic(op); return ; diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 10155d9c7d..aa54410c93 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -98,6 +98,7 @@ allocate: // Still nothing. Let's spill a reg and goto 10. // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? int bestToSpill = -1; for (int i = 0; i < allocCount; i++) { int reg = allocOrder[i]; @@ -108,7 +109,7 @@ allocate: } if (bestToSpill != -1) { - ERROR_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill); + // ERROR_LOG(JIT, "Out of registers at PC %08x - spills register %i.", mips_->pc, bestToSpill); FlushArmReg((ARMReg)bestToSpill); goto allocate; } From 3e4840df0c874753672288ffbc9046e134cf2ddf Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 23:41:26 +0100 Subject: [PATCH 29/49] Don't lock the audio mutex if sound is off --- Core/HLE/__sceAudio.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Core/HLE/__sceAudio.cpp b/Core/HLE/__sceAudio.cpp index 48261e457b..89338fb447 100644 --- a/Core/HLE/__sceAudio.cpp +++ b/Core/HLE/__sceAudio.cpp @@ -198,9 +198,8 @@ void __AudioUpdate() } } - section.lock(); - if (g_Config.bEnableSound) { + section.lock(); if (outAudioQueue.room() >= hwBlockSize * 2) { // Push the mixed samples onto the output audio queue. for (int i = 0; i < hwBlockSize; i++) { @@ -210,14 +209,14 @@ void __AudioUpdate() outAudioQueue.push((s16)sampleL); outAudioQueue.push((s16)sampleR); } - } else { - // This happens quite a lot. There's still something slightly off - // about the amount of audio we produce. - DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity()); } + section.unlock(); + } else { + // This happens quite a lot. There's still something slightly off + // about the amount of audio we produce. + DEBUG_LOG(HLE, "Audio outbuffer overrun! room = %i / %i", outAudioQueue.room(), (u32)outAudioQueue.capacity()); } - section.unlock(); } void __AudioSetOutputFrequency(int freq) From 97f3638212432756fe709b232c29a9e5c0621103 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 23:42:46 +0100 Subject: [PATCH 30/49] Add kernelstats Useful for finding out if some syscall takes unexpectedly much CPU. --- Core/HLE/HLE.cpp | 13 +++++++++++++ Core/HLE/sceKernel.cpp | 1 + Core/HLE/sceKernel.h | 22 +++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index 4fcb6b93c1..c12e7b1c63 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include "base/timeutil.h" #include "HLE.h" #include #include @@ -344,6 +345,8 @@ inline void hleFinishSyscall(int modulenum, int funcnum) void CallSyscall(u32 op) { + time_update(); + double start = time_now_d(); u32 callno = (op >> 6) & 0xFFFFF; //20 bits int funcnum = callno & 0xFFF; int modulenum = (callno & 0xFF000) >> 12; @@ -365,4 +368,14 @@ void CallSyscall(u32 op) { ERROR_LOG(HLE,"Unimplemented HLE function %s", moduleDB[modulenum].funcTable[funcnum].name); } + time_update(); + double total = time_now_d() - start; + if (total > kernelStats.slowestSyscallTime) { + const char *name = moduleDB[modulenum].funcTable[funcnum].name; + if (0 != strcmp(name, "_sceKernelIdle")) { + kernelStats.slowestSyscallTime = total; + kernelStats.slowestSyscallName = name; + } + } + kernelStats.msInSyscalls += total; } diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 26909f5574..1dae0dd5ab 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -71,6 +71,7 @@ static bool kernelRunning = false; KernelObjectPool kernelObjects; +KernelStats kernelStats; void __KernelInit() { diff --git a/Core/HLE/sceKernel.h b/Core/HLE/sceKernel.h index 0b77682136..52ad5f8ca2 100644 --- a/Core/HLE/sceKernel.h +++ b/Core/HLE/sceKernel.h @@ -377,6 +377,7 @@ public: return t; } } + template T* GetByModuleByEntryAddr(u32 entryAddr) { @@ -410,13 +411,32 @@ public: int GetCount(); private: - enum {maxCount=4096, handleOffset=0x100}; + enum { + maxCount=4096, + handleOffset=0x100 + }; KernelObject *pool[maxCount]; bool occupied[maxCount]; }; extern KernelObjectPool kernelObjects; +struct KernelStats { + void Reset() { + memset(this, 0, sizeof(this)); + } + void ResetFrame() { + msInSyscalls = 0; + slowestSyscallTime = 0; + slowestSyscallName = 0; + } + + double msInSyscalls; + double slowestSyscallTime; + const char *slowestSyscallName; +}; + +extern KernelStats kernelStats; void Register_ThreadManForUser(); void Register_LoadExecForUser(); From ca0d74762dbcf26ed79722c2bc489bd50c13f32e Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Thu, 10 Jan 2013 23:49:33 +0100 Subject: [PATCH 31/49] Some realtime profiling stuff. Enable FZ (flush-to-zero) mode. --- CMakeLists.txt | 1 + Core/HLE/sceDisplay.cpp | 8 ++++++++ GPU/GLES/DisplayListInterpreter.cpp | 2 +- GPU/GLES/Framebuffer.cpp | 3 ++- GPU/GLES/TextureCache.cpp | 12 +++++++++++- GPU/GLES/TextureCache.h | 1 + GPU/GPUCommon.cpp | 8 ++++++-- GPU/GPUState.h | 2 ++ android/jni/EmuScreen.cpp | 1 + android/jni/NativeApp.cpp | 7 +++++++ native | 2 +- 11 files changed, 41 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9c85ba7a16..fcdda619e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,6 +112,7 @@ if(NOT MSVC) add_definitions(-O2) add_definitions(-Wno-multichar) add_definitions(-fno-strict-aliasing) + add_definitions(-ffast-math) if(NOT APPLE) add_definitions(-Wno-psabi) add_definitions(-D_XOPEN_SOURCE=600 -D_XOPEN_SOURCE_EXTENDED -D__BSD_VISIBLE=1) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index e85be05e2c..d047027cdc 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -211,6 +211,9 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { char stats[512]; sprintf(stats, "Frames: %i\n" + "DL processing time: %0.2f ms\n" + "Kernel processing time: %0.2f ms\n" + "Slowest syscall: %s : %0.2f ms\n" "Draw calls: %i\n" "Draw flushes: %i\n" "Vertices Transformed: %i\n" @@ -221,6 +224,10 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { "Fragment shaders loaded: %i\n" "Combined shaders loaded: %i\n", gpuStats.numFrames, + gpuStats.msProcessingDisplayLists * 1000.0f, + kernelStats.msInSyscalls * 1000.0f, + kernelStats.slowestSyscallName ? kernelStats.slowestSyscallName : "(none)", + kernelStats.slowestSyscallTime * 1000.0f, gpuStats.numDrawCalls, gpuStats.numFlushes, gpuStats.numVertsTransformed, @@ -238,6 +245,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) { PPGeEnd(); gpuStats.resetFrame(); + kernelStats.ResetFrame(); } host->EndFrame(); diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index f1a9d1030d..2d7aee60e2 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -199,7 +199,7 @@ void GLES_GPU::DumpNextFrame() { } void GLES_GPU::BeginFrame() { - TextureCache_Decimate(); + TextureCache_StartFrame(); if (dumpNextFrame_) { NOTICE_LOG(G3D, "DUMPING THIS FRAME"); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index f3a6519fa0..963560db1b 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -166,7 +166,8 @@ void FramebufferManager::DrawActiveTexture(float w, float h, bool flip) { Matrix4x4 ortho; ortho.setOrtho(0, 480, 272, 0, -1, 1); glUniformMatrix4fv(draw2dprogram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); - + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glEnableVertexAttribArray(draw2dprogram->a_position); glEnableVertexAttribArray(draw2dprogram->a_texcoord0); glVertexAttribPointer(draw2dprogram->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 534564187b..14aec11244 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -598,6 +598,13 @@ void convertColors(u8 *finalBuf, GLuint dstFmt, int numPixels) { } } +int lastBoundTexture = -1; + +void TextureCache_StartFrame() { + lastBoundTexture = -1; + TextureCache_Decimate(); +} + void PSPSetTexture() { u32 texaddr = (gstate.texaddr[0] & 0xFFFFF0) | ((gstate.texbufwidth[0]<<8) & 0xFF000000); texaddr &= 0xFFFFFFF; @@ -656,7 +663,10 @@ void PSPSetTexture() { if (match) { //got one! entry.frameCounter = gpuStats.numFrames; - glBindTexture(GL_TEXTURE_2D, entry.texture); + if (entry.texture != lastBoundTexture) { + glBindTexture(GL_TEXTURE_2D, entry.texture); + lastBoundTexture = entry.texture; + } UpdateSamplingParams(entry, false); DEBUG_LOG(G3D, "Texture at %08x Found in Cache, applying", texaddr); return; //Done! diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index 7be01ad2e3..a9e062170f 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -24,6 +24,7 @@ void PSPSetTexture(); void TextureCache_Init(); void TextureCache_Shutdown(); void TextureCache_Clear(bool delete_them); +void TextureCache_StartFrame(); void TextureCache_Decimate(); // Run this once per frame to get rid of old textures. void TextureCache_Invalidate(u32 addr, int size, bool force); void TextureCache_InvalidateAll(bool force); diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index bb877840cf..b55554aa9e 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1,3 +1,4 @@ +#include "base/timeutil.h" #include "../Core/MemMap.h" #include "GeDisasm.h" #include "GPUCommon.h" @@ -41,7 +42,6 @@ u32 GPUCommon::EnqueueList(u32 listpc, u32 stall, int subIntrBase, bool head) void GPUCommon::UpdateStall(int listid, u32 newstall) { - for (auto iter = dlQueue.begin(); iter != dlQueue.end(); ++iter) { DisplayList &cur = *iter; @@ -56,6 +56,8 @@ void GPUCommon::UpdateStall(int listid, u32 newstall) bool GPUCommon::InterpretList(DisplayList &list) { + time_update(); + double start = time_now_d(); currentList = &list; // Reset stackptr for safety stackptr = 0; @@ -91,13 +93,15 @@ bool GPUCommon::InterpretList(DisplayList &list) list.pc += 4; prev = op; } + time_update(); + gpuStats.msProcessingDisplayLists += time_now_d() - start; return true; } bool GPUCommon::ProcessDLQueue() { DisplayListQueue::iterator iter = dlQueue.begin(); - while (!(iter == dlQueue.end())) + while (iter != dlQueue.end()) { DisplayList &l = *iter; DEBUG_LOG(G3D,"Okay, starting DL execution at %08x - stall = %08x", l.pc, l.stall); diff --git a/GPU/GPUState.h b/GPU/GPUState.h index 4b3a58a6a9..dfbd97dec8 100644 --- a/GPU/GPUState.h +++ b/GPU/GPUState.h @@ -263,6 +263,7 @@ struct GPUStatistics numShaderSwitches = 0; numFlushes = 0; numTexturesDecoded = 0; + msProcessingDisplayLists = 0; } // Per frame statistics @@ -274,6 +275,7 @@ struct GPUStatistics int numTextureSwitches; int numShaderSwitches; int numTexturesDecoded; + double msProcessingDisplayLists; // Total statistics, updated by the GPU core in UpdateStats int numFrames; diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index 0dffb2548f..623bec0621 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -41,6 +41,7 @@ EmuScreen::EmuScreen(const std::string &filename) : invalid_(true) { + CheckGLExtensions(); std::string fileToStart = filename; // This is probably where we should start up the emulated PSP. INFO_LOG(BOOT, "Starting up hardware."); diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index 9faf23f009..4877028375 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -31,6 +31,7 @@ #include "gfx/gl_lost_manager.h" #include "gfx/texture.h" #include "input/input_state.h" +#include "math/math_util.h" #include "math/lin/matrix4x4.h" #include "ui/screen.h" #include "ui/ui.h" @@ -155,6 +156,7 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID) { + EnableFZ(); std::string user_data_path = savegame_directory; // We want this to be FIRST. @@ -308,6 +310,11 @@ void NativeInitGraphics() void NativeRender() { + EnableFZ(); + // Clearing the screen at the start of the frame is an optimization for tiled mobile GPUs, as it then doesn't need to keep it around between frames. + glClearColor(0,0,0,1); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + glstate.Restore(); glViewport(0, 0, pixel_xres, pixel_yres); Matrix4x4 ortho; diff --git a/native b/native index c63061ddfd..7ceecd22b4 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit c63061ddfd5aec3b9dd51aa4c71150de905d8d1e +Subproject commit 7ceecd22b421c697d4d8c8ad6bda7654226be8ca From aa59ddad09fa74bc294157e3eabb90638fc49068 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 01:59:26 +0100 Subject: [PATCH 32/49] Merge AsmRoutineManager into Jit. Allows safe short branches. --- Core/MIPS/ARM/ArmAsm.cpp | 15 +++++++++------ Core/MIPS/ARM/ArmAsm.h | 35 ----------------------------------- Core/MIPS/ARM/ArmJit.cpp | 20 ++++++++------------ Core/MIPS/ARM/ArmJit.h | 15 ++++++++++++--- 4 files changed, 29 insertions(+), 56 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 63c5cd52a8..df3814078a 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -60,7 +60,7 @@ static bool enableDebug = false; // R9 : extern volatile CoreState coreState; -void Jit() +void JitAt() { MIPSComp::jit->Compile(currentMIPS->pc); } @@ -79,8 +79,9 @@ void DisassembleArm(const u8 *data, int size); // dynarec buffer // At this offset - 4, there is an int specifying the block number. +namespace MIPSComp { -void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) +void Jit::GenerateFixedCode() { enterCode = AlignCode16(); @@ -93,8 +94,8 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // Fixed registers, these are always kept when in Jit context. // R13 cannot be used as it's the stack pointer. ARMABI_MOVI2R(R11, (u32)Memory::base); - ARMABI_MOVI2R(R10, (u32)mips); - ARMABI_MOVI2R(R9, (u32)jit->GetBlockCache()->GetCodePointers()); + ARMABI_MOVI2R(R10, (u32)mips_); + ARMABI_MOVI2R(R9, (u32)GetBlockCache()->GetCodePointers()); outerLoop = GetCodePtr(); QuickCallFunction(R0, (void *)&CoreTiming::Advance); @@ -140,7 +141,7 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) SetCC(CC_AL); //Ok, no block, let's jit - ARMABI_CallFunction((void *)&Jit); + ARMABI_CallFunction((void *)&JitAt); B(dispatcherNoCheck); // no point in special casing this @@ -165,4 +166,6 @@ void ArmAsmRoutineManager::Generate(MIPSState *mips, MIPSComp::Jit *jit) // Don't forget to zap the instruction cache! FlushIcache(); -} \ No newline at end of file +} + +} // namespace \ No newline at end of file diff --git a/Core/MIPS/ARM/ArmAsm.h b/Core/MIPS/ARM/ArmAsm.h index 3e15919df1..37f4acba04 100644 --- a/Core/MIPS/ARM/ArmAsm.h +++ b/Core/MIPS/ARM/ArmAsm.h @@ -23,40 +23,5 @@ // Runtime generated assembly routines, like the Dispatcher. -namespace MIPSComp -{ - class Jit; -} - -class ArmAsmRoutineManager : public ArmGen::ARMXCodeBlock -{ -public: - ArmAsmRoutineManager() - { - } - ~ArmAsmRoutineManager() - { - FreeCodeSpace(); - } - - void Init(MIPSState *mips, MIPSComp::Jit *jit) - { - AllocCodeSpace(8192); - Generate(mips, jit); - WriteProtect(); - } - - const u8 *enterCode; - - const u8 *outerLoop; - const u8 *dispatcherCheckCoreState; - const u8 *dispatcher; - const u8 *dispatcherNoCheck; - - const u8 *breakpointBailout; - -private: - void Generate(MIPSState *mips, MIPSComp::Jit *jit); -}; #endif // _JIT64ASM_H diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 9931492abd..64b1474ee7 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -57,13 +57,12 @@ namespace MIPSComp Jit::Jit(MIPSState *mips) : blocks(mips), gpr(mips), mips_(mips) { blocks.Init(); - asm_.Init(mips, this); gpr.SetEmitter(this); //fpr.SetEmitter(this); - AllocCodeSpace(1024 * 1024 * 16); + AllocCodeSpace(1024 * 1024 * 16); // 32MB is the absolute max because that's what an ARM branch instruction can reach, backwards and forwards. + GenerateFixedCode(); } - void Jit::FlushAll() { gpr.FlushAll(); @@ -74,6 +73,7 @@ void Jit::ClearCache() { blocks.Clear(); ClearCodeSpace(); + GenerateFixedCode(); } void Jit::CompileAt(u32 addr) @@ -97,7 +97,7 @@ void Jit::Compile(u32 em_address) void Jit::RunLoopUntil(u64 globalticks) { // TODO: copy globalticks somewhere - ((void (*)())asm_.enterCode)(); + ((void (*)())enterCode)(); } const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) @@ -115,8 +115,7 @@ const u8 *Jit::DoJit(u32 em_address, ArmJitBlock *b) SetCC(CC_LT); ARMABI_MOVI2R(R0, js.blockStart); MovToPC(R0); - ARMABI_MOVI2R(R0, (u32)asm_.outerLoop); // downcount hit zero - go advance. - B(R0); + B((const void *)outerLoop); SetCC(CC_AL); b->normalEntry = GetCodePtr(); @@ -239,8 +238,7 @@ void Jit::WriteExit(u32 destination, int exit_num) } else { ARMABI_MOVI2R(R0, destination); MovToPC(R0); - ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); - B(R0); + B((const void *)dispatcher); } } @@ -249,15 +247,13 @@ void Jit::WriteExitDestInR(ARMReg Reg) MovToPC(Reg); DoDownCount(); // TODO: shouldn't need an indirect branch here... - ARMABI_MOVI2R(R0, (u32)asm_.dispatcher); - B(R0); + B((const void *)dispatcher); } void Jit::WriteSyscallExit() { DoDownCount(); - ARMABI_MOVI2R(R0, (u32)asm_.dispatcherCheckCoreState); - B(R0); + B((const void *)dispatcherCheckCoreState); } diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 3fefdf68a7..205e9c119d 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -88,13 +88,14 @@ public: void Comp_mxc1(u32 op); ArmJitBlockCache *GetBlockCache() { return &blocks; } - ArmAsmRoutineManager &Asm() { return asm_; } void ClearCache(); private: + void GenerateFixedCode(); void FlushAll(); + // TODO: Split into two parts, the first part can be shared in branches. void DoDownCount(); void MovFromPC(ARMReg r); void MovToPC(ARMReg r); @@ -128,9 +129,17 @@ private: ArmRegCache gpr; // FPURegCache fpr; - ArmAsmRoutineManager asm_; - MIPSState *mips_; + + // Code pointers + const u8 *enterCode; + + const u8 *outerLoop; + const u8 *dispatcherCheckCoreState; + const u8 *dispatcher; + const u8 *dispatcherNoCheck; + + const u8 *breakpointBailout; }; typedef void (Jit::*MIPSCompileFunc)(u32 opcode); From ba804cfa89ba3515336c5f955358e8326264e33e Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 02:28:02 +0100 Subject: [PATCH 33/49] Cleanup ArmAsm, add idea --- Core/MIPS/ARM/ArmAsm.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index df3814078a..7f345e231f 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -103,7 +103,6 @@ void Jit::GenerateFixedCode() dispatcherCheckCoreState = GetCodePtr(); - // TODO: critical ARMABI_MOVI2R(R0, (u32)&coreState); LDR(R0, R0); CMP(R0, 0); @@ -130,6 +129,8 @@ void Jit::GenerateFixedCode() BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF ADD(R0, R0, R11); // TODO: Optimize (can merge with next instr) LDR(R0, R0); + // This could replace the above two instructions if the emitter wasn't bugged: + // LDR(R0, R11, Operand2(R0)); AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); @@ -147,7 +148,6 @@ void Jit::GenerateFixedCode() SetJumpTarget(bail); - // TODO: critical ARMABI_MOVI2R(R0, (u32)&coreState); LDR(R0, R0); CMP(R0, 0); From aab792ea0bbe89224b66a4048ef5e4fb5056d890 Mon Sep 17 00:00:00 2001 From: Sacha Date: Fri, 11 Jan 2013 19:57:23 +1000 Subject: [PATCH 34/49] Bring Symbian to the JIT party. Hardcode CPU features (must be ARMv6 single-core). --- Common/ArmCPUDetect.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp index 453c580097..aba8fe2638 100644 --- a/Common/ArmCPUDetect.cpp +++ b/Common/ArmCPUDetect.cpp @@ -20,7 +20,7 @@ #include "StringUtil.h" // Only Linux platforms have /proc/cpuinfo -#if !defined(BLACKBERRY) && !defined(IOS) +#if !defined(BLACKBERRY) && !defined(IOS) && !defined(__SYMBIAN32__) const char procfile[] = "/proc/cpuinfo"; char *GetCPUString() @@ -73,7 +73,9 @@ bool CheckCPUFeature(const char *feature) #endif int GetCoreCount() { -#if defined(BLACKBERRY) || defined(IOS) +#ifdef __SYMBIAN32__ + return 1; +#elif defined(BLACKBERRY) || defined(IOS) return 2; #else const char marker[] = "processor\t: "; @@ -114,8 +116,7 @@ void CPUInfo::Detect() // Get the information about the CPU num_cores = GetCoreCount(); -// Hardcode this for now -#if !defined(BLACKBERRY) && !defined(IOS) +#if !defined(BLACKBERRY) && !defined(IOS) && !defined(__SYMBIAN32__) strncpy(cpu_string, GetCPUString(), sizeof(cpu_string)); bSwp = CheckCPUFeature("swp"); bHalf = CheckCPUFeature("half"); @@ -130,18 +131,24 @@ void CPUInfo::Detect() bVFPv4 = CheckCPUFeature("vfpv4"); bIDIVa = CheckCPUFeature("idiva"); bIDIVt = CheckCPUFeature("idivt"); - // These two are ARMv8 specific. + // These two require ARMv8 or higher bFP = CheckCPUFeature("fp"); bASIMD = CheckCPUFeature("asimd"); #else + // Hardcode this for now bSwp = true; bHalf = true; bThumb = false; bFastMult = true; bVFP = true; bEDSP = true; +#ifdef __SYMBIAN32__ + bThumbEE = false; + bNEON = false; +#else bThumbEE = true; bNEON = true; +#endif bVFPv3 = true; bTLS = true; bVFPv4 = false; @@ -156,7 +163,7 @@ void CPUInfo::Detect() std::string CPUInfo::Summarize() { std::string sum; -#if defined(BLACKBERRY) || defined(IOS) +#if defined(BLACKBERRY) || defined(IOS) || defined(__SYMBIAN32__) sum = StringFromFormat("%i cores", num_cores); #else if (num_cores == 1) From 51fd0250647300c97e5445bede2105aff8457965 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 15:20:06 +0100 Subject: [PATCH 35/49] Add ARMv6 fallback to jit (it doesn't have MOVW/MOVT) --- Common/ArmABI.cpp | 24 ------------------------ Common/ArmCPUDetect.cpp | 8 ++++++++ Common/ArmEmitter.cpp | 38 ++++++++++++++++++++++++++++++++++++++ Common/ArmEmitter.h | 2 -- Common/CPUDetect.h | 2 ++ 5 files changed, 48 insertions(+), 26 deletions(-) diff --git a/Common/ArmABI.cpp b/Common/ArmABI.cpp index a854c3e983..688f54d381 100644 --- a/Common/ArmABI.cpp +++ b/Common/ArmABI.cpp @@ -76,30 +76,6 @@ void ARMXEmitter::ARMABI_PopAllCalleeSavedRegsAndAdjustStack() { POP(4, R0, R1, R2, R3); } -void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, u32 val) -{ - Operand2 op2; - bool inverse; - if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { - if (!inverse) - MOV(reg, op2); - else - MVN(reg, op2); - } else { - MOVW(reg, val); - if(val & 0xFFFF0000) - MOVT(reg, val, true); - } -} -// Moves IMM to memory location -void ARMXEmitter::ARMABI_MOVI2M(Operand2 op, Operand2 val) -{ - // This moves imm to a memory location - MOVW(R14, val); MOVT(R14, val, true); - MOVW(R12, op); MOVT(R12, op, true); - STR(R12, R14); // R10 is what we want to store -} - const char *conditions[] = {"EQ", "NEQ", "CS", "CC", "MI", "PL", "VS", "VC", "HI", "LS", "GE", "LT", "GT", "LE", "AL" }; static void ShowCondition(u32 cond) { diff --git a/Common/ArmCPUDetect.cpp b/Common/ArmCPUDetect.cpp index 453c580097..59670eaca2 100644 --- a/Common/ArmCPUDetect.cpp +++ b/Common/ArmCPUDetect.cpp @@ -41,6 +41,7 @@ char *GetCPUString() continue; cpu_string = buf + sizeof(marker) - 1; cpu_string = strndup(cpu_string, strlen(cpu_string) - 1); // Strip the newline + // INFO_LOG(BOOT, "CPU: %s", cpu_string); break; } return cpu_string; @@ -150,6 +151,13 @@ void CPUInfo::Detect() bFP = false; bASIMD = false; #endif +// On android, we build a separate library for ARMv7 so this is fine. +// TODO: Check for ARMv7 on other platforms. +#if defined(__ARM_ARCH_7A__) + bArmV7 = true; +#else + bArmV7 = false; +#endif } // Turn the cpu info into a string we can show diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 83d50346ad..92c24b63c2 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -78,6 +78,44 @@ bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated) } } +void ARMXEmitter::ARMABI_MOVI2R(ARMReg reg, u32 val) +{ + Operand2 op2; + bool inverse; + if (TryMakeOperand2_AllowInverse(val, op2, &inverse)) { + if (!inverse) + MOV(reg, op2); + else + MVN(reg, op2); + } else { + if (cpu_info.bArmV7) { + // ARMv7 - can use MOVT/MOVW, best choice + MOVW(reg, val & 0xFFFF); + if(val & 0xFFFF0000) + MOVT(reg, val, true); + } else { + // ARMv6 - fallback sequence. + // TODO: Optimize further. Can for example choose negation etc. + // Literal pools is another way to do this but much more complicated + // so I can't really be bothered for an outdated CPU architecture like ARMv6. + bool first = true; + int shift = 16; + for (int i = 0; i < 4; i++) { + if (val & 0xFF) { + if (first) { + MOV(reg, Operand2((u8)val, (u8)(shift & 0xF))); + first = false; + } else { + ORR(reg, reg, Operand2((u8)val, (u8)(shift & 0xF))); + } + } + shift -= 4; + val >>= 8; + } + } + } +} + void ARMXEmitter::QuickCallFunction(ARMReg reg, void *func) { ARMABI_MOVI2R(reg, (u32)(func)); BL(reg); diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 459c4231f4..da0e1efa63 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -481,8 +481,6 @@ public: void ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); void ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); void ARMABI_MOVI2R(ARMReg reg, u32 val); - void ARMABI_MOVI2M(Operand2 op, Operand2 val); - void ARMABI_MOVI2M(u32 addr, Operand2 val); void ARMABI_ShowConditions(); void ARMABI_Return(); diff --git a/Common/CPUDetect.h b/Common/CPUDetect.h index 858c57e680..4ca4712bb6 100644 --- a/Common/CPUDetect.h +++ b/Common/CPUDetect.h @@ -71,6 +71,8 @@ struct CPUInfo bool bVFPv4; bool bIDIVa; bool bIDIVt; + bool bArmV7; // enable MOVT, MOVW etc + // ARMv8 specific bool bFP; bool bASIMD; From 9b791b9953b7602ac5c3dc6c8af0b2b66fe6bc3a Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 15:22:31 +0100 Subject: [PATCH 36/49] More ARMJIT optimization --- Core/MIPS/ARM/ArmCompALU.cpp | 43 +++++++++++++++++------------- Core/MIPS/ARM/ArmCompLoadStore.cpp | 33 ++++++++++++----------- Core/MIPS/ARM/ArmJit.cpp | 5 +++- Core/MIPS/ARM/ArmRegCache.cpp | 25 ++++++++++++++++- Core/MIPS/ARM/ArmRegCache.h | 2 ++ 5 files changed, 73 insertions(+), 35 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index c50c0e497b..8b522b812c 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -119,11 +119,7 @@ namespace MIPSComp break; case 11: // R(rt) = R(rs) < uimm; break; //sltiu - gpr.SpillLock(rt, rs); - gpr.MapReg(rs); - gpr.MapReg(rt, MAP_DIRTY); - gpr.ReleaseSpillLocks(); - + gpr.MapDirtyIn(rt, rs); { Operand2 op2; if (TryMakeOperand2(uimm, op2)) { @@ -174,13 +170,7 @@ namespace MIPSComp int rt = _RT; int rs = _RS; int rd = _RD; - - gpr.SpillLock(rt, rs, rd); - gpr.MapReg(rt); - gpr.MapReg(rs); - gpr.MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases - gpr.ReleaseSpillLocks(); - + switch (op & 63) { //case 10: if (!R(rt)) R(rd) = R(rs); break; //movz @@ -188,28 +178,44 @@ namespace MIPSComp // case 32: //R(rd) = R(rs) + R(rt); break; //add case 33: //R(rd) = R(rs) + R(rt); break; //addu - ADD(gpr.R(rd), gpr.R(rs), gpr.R(rt)); + // Some optimized special cases + if (rs == 0) { + gpr.MapDirtyIn(rd, rt); + MOV(gpr.R(rd), gpr.R(rt)); + } else if (rt == 0) { + gpr.MapDirtyIn(rd, rs); + MOV(gpr.R(rd), gpr.R(rs)); + } else { + gpr.MapDirtyInIn(rd, rs, rt); + ADD(gpr.R(rd), gpr.R(rs), gpr.R(rt)); + } break; - case 134: //R(rd) = R(rs) - R(rt); break; //sub - case 135: + case 34: //R(rd) = R(rs) - R(rt); break; //sub + case 35: + gpr.MapDirtyInIn(rd, rs, rt); SUB(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 136: //R(rd) = R(rs) & R(rt); break; //and + case 36: //R(rd) = R(rs) & R(rt); break; //and + gpr.MapDirtyInIn(rd, rs, rt); AND(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 137: //R(rd) = R(rs) | R(rt); break; //or + case 37: //R(rd) = R(rs) | R(rt); break; //or + gpr.MapDirtyInIn(rd, rs, rt); ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; - case 138: //R(rd) = R(rs) ^ R(rt); break; //xor/eor + case 38: //R(rd) = R(rs) ^ R(rt); break; //xor/eor + gpr.MapDirtyInIn(rd, rs, rt); EOR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); break; case 39: // R(rd) = ~(R(rs) | R(rt)); //nor + gpr.MapDirtyInIn(rd, rs, rt); ORR(gpr.R(rd), gpr.R(rs), gpr.R(rt)); MVN(gpr.R(rd), gpr.R(rd)); break; case 42: //R(rd) = (int)R(rs) < (int)R(rt); break; //slt + gpr.MapDirtyInIn(rd, rs, rt); CMP(gpr.R(rs), gpr.R(rt)); SetCC(CC_LT); ARMABI_MOVI2R(gpr.R(rd), 1); @@ -219,6 +225,7 @@ namespace MIPSComp break; case 43: //R(rd) = R(rs) < R(rt); break; //sltu + gpr.MapDirtyInIn(rd, rs, rt); CMP(gpr.R(rs), gpr.R(rt)); SetCC(CC_LO); ARMABI_MOVI2R(gpr.R(rd), 1); diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 524c6fdb7d..782bfc04ec 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -47,11 +47,11 @@ namespace MIPSComp switch (o) { case 37: //R(rt) = ReadMem16(addr); break; //lhu - case 36: //R(rt) = ReadMem8 (addr); break; //lbu Comp_Generic(op); return; - case 35: //R(rt) = ReadMem32(addr); break; //lw + case 35: //R(rt) = ReadMem32(addr); //lw + case 36: //R(rt) = ReadMem8 (addr); break; //lbu if (true || g_Config.bFastMemory) { gpr.SpillLock(rt, rs); gpr.MapReg(rt, MAP_DIRTY); @@ -66,24 +66,23 @@ namespace MIPSComp ADD(R0, gpr.R(rs), R0); } BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF - ADD(R0, R0, R11); - LDR(gpr.R(rt), R0); + ADD(R0, R0, R11); // TODO: Merge with next instruction + if (o == 35) { + LDR(gpr.R(rt), R0); + } else if (o == 36) { + LDRB(gpr.R(rt), R0); + } } else { Comp_Generic(op); return; } break; - case 132: //R(rt) = (u32)(s32)(s8) ReadMem8 (addr); break; //lb - case 133: //R(rt) = (u32)(s32)(s16)ReadMem16(addr); break; //lh - case 136: //R(rt) = ReadMem8 (addr); break; //lbu - case 140: //WriteMem8 (addr, R(rt)); break; //sb - - case 40: case 41: //WriteMem16(addr, R(rt)); break; //sh Comp_Generic(op); return; + case 40: //sb case 43: //WriteMem32(addr, R(rt)); break; //sw if (true || g_Config.bFastMemory) { gpr.SpillLock(rt, rs); @@ -100,7 +99,11 @@ namespace MIPSComp } BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF ADD(R0, R0, R11); - STR(R0, gpr.R(rt)); + if (o == 43) { + STR(R0, gpr.R(rt)); + } else if (o == 40) { + STRB(R0, gpr.R(rt)); + } } else { Comp_Generic(op); return; @@ -108,7 +111,7 @@ namespace MIPSComp break; // break; /* - case 134: //lwl + case 34: //lwl { Crash(); //u32 shift = (addr & 3) << 3; @@ -117,7 +120,7 @@ namespace MIPSComp } break; - case 138: //lwr + case 38: //lwr { Crash(); //u32 shift = (addr & 3) << 3; @@ -127,7 +130,7 @@ namespace MIPSComp } break; - case 142: //swl + case 42: //swl { Crash(); //u32 shift = (addr & 3) << 3; @@ -136,7 +139,7 @@ namespace MIPSComp // ( mem & (0xffffff00 << shift) )); } break; - case 146: //swr + case 46: //swr { Crash(); // u32 shift = (addr & 3) << 3; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 64b1474ee7..158b4179f2 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -220,7 +220,10 @@ void Jit::DoDownCount() } } - +// IDEA - could have a WriteDualExit that takes two destinations and two condition flags, +// and just have conditional that set PC "twice". This only works when we fall back to dispatcher +// though, as we need to have the SUBS flag set in the end. So with block linking in the mix, +// I don't think this gives us that much benefit. void Jit::WriteExit(u32 destination, int exit_num) { DoDownCount(); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index aa54410c93..14587b4562 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -83,7 +83,13 @@ allocate: ar[reg].isDirty = (mapFlags & MAP_DIRTY) ? true : false; if (!(mapFlags & MAP_NOINIT)) { if (mr[mipsReg].loc == ML_MEM) { - emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); + if (mipsReg != 0) { + emit->LDR((ARMReg)reg, CTXREG, 4 * mipsReg); + } else { + // If we get a request to load the zero register, at least we won't spend + // time on a memory access... + emit->MOV((ARMReg)reg, 0); + } } else if (mr[mipsReg].loc == ML_IMM) { emit->ARMABI_MOVI2R((ARMReg)reg, mr[mipsReg].imm); ar[reg].isDirty = true; // IMM is always dirty. @@ -119,6 +125,23 @@ allocate: return INVALID_REG; } +void ArmRegCache::MapDirtyIn(MIPSReg rd, MIPSReg rs) { + SpillLock(rd, rs); + // bool overlap == rd == rs + MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases + MapReg(rs); + ReleaseSpillLocks(); +} + +void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt) { + SpillLock(rd, rs, rt); + // bool overlap == rd == rs || rt == rd || rs == rt; + MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases + MapReg(rt); + MapReg(rs); + ReleaseSpillLocks(); +} + void ArmRegCache::FlushArmReg(ARMReg r) { if (ar[r].mipsReg == -1) { // Nothing to do, reg not mapped. diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 8154c93c8f..8bdaa8a40c 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -85,6 +85,8 @@ public: // Returns an ARM register containing the requested MIPS register. ARMReg MapReg(MIPSReg reg, int mapFlags = 0); + void MapDirtyIn(MIPSReg rd, MIPSReg rs); + void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt); void FlushArmReg(ARMReg r); void FlushMipsReg(MIPSReg r); From bc9c3db3038e20bc7cde4344ae0ca7fd425d14f1 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 17:25:54 +0100 Subject: [PATCH 37/49] Armjit: Add option for fastmem. Small optimization. --- Core/MIPS/ARM/ArmCompALU.cpp | 30 +++++++------------- Core/MIPS/ARM/ArmCompBranch.cpp | 3 +- Core/MIPS/ARM/ArmCompLoadStore.cpp | 44 +++++++++++++++--------------- Core/MIPS/ARM/ArmRegCache.cpp | 19 +++++++++---- Core/MIPS/ARM/ArmRegCache.h | 5 ++-- android/jni/MenuScreens.cpp | 6 ++-- 6 files changed, 54 insertions(+), 53 deletions(-) diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 8b522b812c..3b4127dd67 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -43,10 +43,7 @@ namespace MIPSComp if (gpr.IsImm(rs)) { gpr.SetImm(rt, (*eval)(gpr.GetImm(rs), uimm)); } else { - gpr.SpillLock(rs, rt); - gpr.MapReg(rt, MAP_DIRTY); - gpr.MapReg(rs); - gpr.ReleaseSpillLocks(); + gpr.MapDirtyIn(rt, rs); // TODO: Special case when uimm can be represented as an Operand2 Operand2 op2; if (TryMakeOperand2(uimm, op2)) { @@ -76,13 +73,14 @@ namespace MIPSComp } else if (rs == 0) { // add to zero register = immediate gpr.SetImm(rt, (u32)simm); } else { - gpr.SpillLock(rs, rt); - gpr.MapReg(rt, MAP_DIRTY); - gpr.MapReg(rs); - gpr.ReleaseSpillLocks(); + gpr.MapDirtyIn(rt, rs); Operand2 op2; - if (TryMakeOperand2(simm, op2)) { - ADD(gpr.R(rt), gpr.R(rs), op2); + bool negated; + if (TryMakeOperand2_AllowNegation(simm, op2, &negated)) { + if (!negated) + ADD(gpr.R(rt), gpr.R(rs), op2); + else + SUB(gpr.R(rt), gpr.R(rs), op2); } else { ARMABI_MOVI2R(R0, (u32)simm); ADD(gpr.R(rt), gpr.R(rs), R0); @@ -96,11 +94,7 @@ namespace MIPSComp case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &EvalXor); break; case 10: // R(rt) = (s32)R(rs) < simm; break; //slti - gpr.SpillLock(rt, rs); - gpr.MapReg(rs); - gpr.MapReg(rt, MAP_DIRTY); - gpr.ReleaseSpillLocks(); - + gpr.MapDirtyIn(rt, rs); { Operand2 op2; if (TryMakeOperand2(simm, op2)) { @@ -253,11 +247,7 @@ namespace MIPSComp int rt = _RT; int sa = _SA; - gpr.SpillLock(rd, rt); - gpr.MapReg(rt); - gpr.MapReg(rd, MAP_DIRTY); - gpr.ReleaseSpillLocks(); - + gpr.MapDirtyIn(rd, rt); MOV(gpr.R(rd), Operand2(sa, shiftType, gpr.R(rt))); } diff --git a/Core/MIPS/ARM/ArmCompBranch.cpp b/Core/MIPS/ARM/ArmCompBranch.cpp index 20d1fa18b2..32d75392b6 100644 --- a/Core/MIPS/ARM/ArmCompBranch.cpp +++ b/Core/MIPS/ARM/ArmCompBranch.cpp @@ -425,7 +425,8 @@ void Jit::Comp_Syscall(u32 op) { FlushAll(); - ARMABI_CallFunctionC((void *)&CallSyscall, op); + ARMABI_MOVI2R(R0, op); + QuickCallFunction(R1, (void *)&CallSyscall); WriteSyscallExit(); js.compiling = false; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 782bfc04ec..1fdba9d5fd 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -52,20 +52,20 @@ namespace MIPSComp case 35: //R(rt) = ReadMem32(addr); //lw case 36: //R(rt) = ReadMem8 (addr); break; //lbu - if (true || g_Config.bFastMemory) { - gpr.SpillLock(rt, rs); - gpr.MapReg(rt, MAP_DIRTY); - gpr.MapReg(rs); - gpr.ReleaseSpillLocks(); - + if (g_Config.bFastMemory) { + gpr.MapDirtyIn(rt, rs); Operand2 op2; - if (TryMakeOperand2(offset, op2)) { - ADD(R0, gpr.R(rs), op2); + if (offset) { + if (TryMakeOperand2(offset, op2)) { + ADD(R0, gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); + BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF } - BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF ADD(R0, R0, R11); // TODO: Merge with next instruction if (o == 35) { LDR(gpr.R(rt), R0); @@ -84,20 +84,20 @@ namespace MIPSComp case 40: //sb case 43: //WriteMem32(addr, R(rt)); break; //sw - if (true || g_Config.bFastMemory) { - gpr.SpillLock(rt, rs); - gpr.MapReg(rt); - gpr.MapReg(rs); - gpr.ReleaseSpillLocks(); - + if (g_Config.bFastMemory) { + gpr.MapInIn(rt, rs); Operand2 op2; - if (TryMakeOperand2(offset, op2)) { - ADD(R0, gpr.R(rs), op2); + if (offset) { + if (TryMakeOperand2(offset, op2)) { + ADD(R0, gpr.R(rs), op2); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); + BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF } - BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF ADD(R0, R0, R11); if (o == 43) { STR(R0, gpr.R(rt)); diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 14587b4562..277fbb33b0 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -125,18 +125,25 @@ allocate: return INVALID_REG; } -void ArmRegCache::MapDirtyIn(MIPSReg rd, MIPSReg rs) { +void ArmRegCache::MapInIn(MIPSReg rd, MIPSReg rs) { SpillLock(rd, rs); - // bool overlap == rd == rs - MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases + MapReg(rd); MapReg(rs); ReleaseSpillLocks(); } -void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt) { +void ArmRegCache::MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad) { + SpillLock(rd, rs); + bool overlap = avoidLoad && rd == rs; + MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); + MapReg(rs); + ReleaseSpillLocks(); +} + +void ArmRegCache::MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad) { SpillLock(rd, rs, rt); - // bool overlap == rd == rs || rt == rd || rs == rt; - MapReg(rd, MAP_DIRTY); // can get rid of INITVAL in some cases + bool overlap = avoidLoad && (rd == rs || rd == rt); + MapReg(rd, MAP_DIRTY | (overlap ? 0 : MAP_NOINIT)); MapReg(rt); MapReg(rs); ReleaseSpillLocks(); diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 8bdaa8a40c..691bb9d0c7 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -85,8 +85,9 @@ public: // Returns an ARM register containing the requested MIPS register. ARMReg MapReg(MIPSReg reg, int mapFlags = 0); - void MapDirtyIn(MIPSReg rd, MIPSReg rs); - void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt); + void MapInIn(MIPSReg rd, MIPSReg rs); + void MapDirtyIn(MIPSReg rd, MIPSReg rs, bool avoidLoad = true); + void MapDirtyInIn(MIPSReg rd, MIPSReg rs, MIPSReg rt, bool avoidLoad = true); void FlushArmReg(ARMReg r); void FlushMipsReg(MIPSReg r); diff --git a/android/jni/MenuScreens.cpp b/android/jni/MenuScreens.cpp index af4d878490..6d5759bdfd 100644 --- a/android/jni/MenuScreens.cpp +++ b/android/jni/MenuScreens.cpp @@ -287,10 +287,12 @@ void SettingsScreen::render() { g_Config.iWindowZoom = doubleRes ? 2 : 1; } UICheckBox(GEN_ID, x, y += stride, "Hardware Transform", ALIGN_TOPLEFT, &g_Config.bHardwareTransform); - UICheckBox(GEN_ID, x, y += stride, "Use VBO for drawing", ALIGN_TOPLEFT, &g_Config.bUseVBO); + UICheckBox(GEN_ID, x, y += stride, "Draw using VBO", ALIGN_TOPLEFT, &g_Config.bUseVBO); bool useJit = g_Config.iCpuCore == CPU_JIT; - UICheckBox(GEN_ID, x, y += stride, "Use Dynarec (JIT)", ALIGN_TOPLEFT, &useJit); + UICheckBox(GEN_ID, x, y += stride, "JIT (Dynarec)", ALIGN_TOPLEFT, &useJit); + if (g_Config.iCpuCore == CPU_JIT) + UICheckBox(GEN_ID, x + 350, y, "Fastmem (unstable)", ALIGN_TOPLEFT, &g_Config.bFastMemory); g_Config.iCpuCore = useJit ? CPU_JIT : CPU_INTERPRETER; // ui_draw2d.DrawText(UBUNTU48, "much faster JIT coming later", x, y+=50, 0xcFFFFFFF, ALIGN_LEFT); UICheckBox(GEN_ID, x, y += stride, "On-screen Touch Controls", ALIGN_TOPLEFT, &g_Config.bShowTouchControls); From 37be2600bedd8f4ab4c77905739095cceb79fac2 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 18:50:05 +0100 Subject: [PATCH 38/49] Shave some more instructions --- Core/MIPS/ARM/ArmAsm.cpp | 5 +---- Core/MIPS/ARM/ArmCompLoadStore.cpp | 28 ++++++++++++++++++++-------- 2 files changed, 21 insertions(+), 12 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 7f345e231f..12cf336764 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -127,10 +127,7 @@ void Jit::GenerateFixedCode() LDR(R0, R10, offsetof(MIPSState, pc)); BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF - ADD(R0, R0, R11); // TODO: Optimize (can merge with next instr) - LDR(R0, R0); - // This could replace the above two instructions if the emitter wasn't bugged: - // LDR(R0, R11, Operand2(R0)); + LDR(R0, R11, R0, true, true); AND(R1, R0, Operand2(0xFC, 4)); // rotation is to the right, in 2-bit increments. BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 1fdba9d5fd..9b336550b3 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -59,17 +59,23 @@ namespace MIPSComp if (TryMakeOperand2(offset, op2)) { ADD(R0, gpr.R(rs), op2); } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); + // Try to avoid using MOVT + if (offset < 0) { + ARMABI_MOVI2R(R0, (u32)(-offset)); + SUB(R0, gpr.R(rs), R0); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } } BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF } else { BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF } - ADD(R0, R0, R11); // TODO: Merge with next instruction if (o == 35) { - LDR(gpr.R(rt), R0); + LDR(gpr.R(rt), R11, R0, true, true); } else if (o == 36) { + ADD(R0, R0, R11); // TODO: Merge with next instruction LDRB(gpr.R(rt), R0); } } else { @@ -91,17 +97,23 @@ namespace MIPSComp if (TryMakeOperand2(offset, op2)) { ADD(R0, gpr.R(rs), op2); } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); + // Try to avoid using MOVT + if (offset < 0) { + ARMABI_MOVI2R(R0, (u32)(-offset)); + SUB(R0, gpr.R(rs), R0); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } } BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF } else { BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF } - ADD(R0, R0, R11); if (o == 43) { - STR(R0, gpr.R(rt)); + STR(R0, gpr.R(rt), R11, true, true); } else if (o == 40) { + ADD(R0, R0, R11); STRB(R0, gpr.R(rt)); } } else { From 551bf7b0ad31570f25f63f4d132da0f33fa3b2be Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 19:40:33 +0100 Subject: [PATCH 39/49] Let's build for both armeabi and armeabi-v7a again. --- Windows/PPSSPP.vcxproj | 3 ++- Windows/PPSSPP.vcxproj.filters | 3 +++ android/jni/Application.mk | 5 ++--- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Windows/PPSSPP.vcxproj b/Windows/PPSSPP.vcxproj index 5fc3440272..019fde5bcc 100644 --- a/Windows/PPSSPP.vcxproj +++ b/Windows/PPSSPP.vcxproj @@ -336,6 +336,7 @@ + @@ -374,4 +375,4 @@ - + \ No newline at end of file diff --git a/Windows/PPSSPP.vcxproj.filters b/Windows/PPSSPP.vcxproj.filters index ba3dce5c47..d2538d562b 100644 --- a/Windows/PPSSPP.vcxproj.filters +++ b/Windows/PPSSPP.vcxproj.filters @@ -210,6 +210,9 @@ Android + + Android + diff --git a/android/jni/Application.mk b/android/jni/Application.mk index 62367cb819..d18d3a46ff 100644 --- a/android/jni/Application.mk +++ b/android/jni/Application.mk @@ -1,5 +1,4 @@ APP_STL := stlport_static -APP_ABI := armeabi-v7a +APP_ABI := armeabi-v7a armeabi - -#armeabi +#APP_ABI := armeabi-v7a From 36eaabd91751c6d94cccbd2572687747494da1aa Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 23:42:58 +0100 Subject: [PATCH 40/49] Some constant propagation. Comment out sltiu which causes problems aiming in puzbob (???) --- Common/ArmEmitter.h | 4 +- Core/MIPS/ARM/ArmCompALU.cpp | 32 +++++------ Core/MIPS/ARM/ArmCompLoadStore.cpp | 92 ++++++++++++++++++------------ 3 files changed, 74 insertions(+), 54 deletions(-) diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index da0e1efa63..8307841894 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -81,8 +81,8 @@ enum CCFlags CC_GT, // Signed greater than CC_LE, // Signed less than or equal CC_AL, // Always (unconditional) 14 - CC_HS = 2, // Alias of CC_CS - CC_LO = 3, // Alias of CC_CC + CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same + CC_LO = CC_CC, // Alias of CC_CC Unsigned lower }; const u32 NO_COND = 0xE0000000; diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index 3b4127dd67..e2e96f0456 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -58,7 +58,7 @@ namespace MIPSComp void Jit::Comp_IType(u32 op) { s32 simm = (s32)(s16)(op & 0xFFFF); // sign extension - u32 uimm = (u16)(op & 0xFFFF); + u32 uimm = op & 0xFFFF; int rt = _RT; int rs = _RS; @@ -94,8 +94,8 @@ namespace MIPSComp case 14: CompImmLogic(rs, rt, uimm, &ARMXEmitter::EOR, &EvalXor); break; case 10: // R(rt) = (s32)R(rs) < simm; break; //slti - gpr.MapDirtyIn(rt, rs); { + gpr.MapDirtyIn(rt, rs); Operand2 op2; if (TryMakeOperand2(simm, op2)) { CMP(gpr.R(rs), op2); @@ -103,18 +103,17 @@ namespace MIPSComp ARMABI_MOVI2R(R0, simm); CMP(gpr.R(rs), R0); } + SetCC(CC_LT); + ARMABI_MOVI2R(gpr.R(rt), 1); + SetCC(CC_GE); + ARMABI_MOVI2R(gpr.R(rt), 0); + SetCC(CC_AL); } - - SetCC(CC_LT); - ARMABI_MOVI2R(gpr.R(rt), 1); - SetCC(CC_GE); - ARMABI_MOVI2R(gpr.R(rt), 0); - SetCC(CC_AL); break; - + /* case 11: // R(rt) = R(rs) < uimm; break; //sltiu - gpr.MapDirtyIn(rt, rs); { + gpr.MapDirtyIn(rt, rs); Operand2 op2; if (TryMakeOperand2(uimm, op2)) { CMP(gpr.R(rs), op2); @@ -122,15 +121,14 @@ namespace MIPSComp ARMABI_MOVI2R(R0, uimm); CMP(gpr.R(rs), R0); } + SetCC(CC_LO); + ARMABI_MOVI2R(gpr.R(rt), 1); + SetCC(CC_HS); + ARMABI_MOVI2R(gpr.R(rt), 0); + SetCC(CC_AL); } - - SetCC(CC_LO); - ARMABI_MOVI2R(gpr.R(rt), 1); - SetCC(CC_HS); - ARMABI_MOVI2R(gpr.R(rt), 0); - SetCC(CC_AL); break; - + */ case 15: // R(rt) = uimm << 16; //lui gpr.SetImm(rt, uimm << 16); break; diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 9b336550b3..5479580c98 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -50,27 +50,38 @@ namespace MIPSComp Comp_Generic(op); return; - case 35: //R(rt) = ReadMem32(addr); //lw + case 35: //R(rt) = ReadMem32(addr); //lw case 36: //R(rt) = ReadMem8 (addr); break; //lbu if (g_Config.bFastMemory) { - gpr.MapDirtyIn(rt, rs); - Operand2 op2; - if (offset) { - if (TryMakeOperand2(offset, op2)) { - ADD(R0, gpr.R(rs), op2); - } else { - // Try to avoid using MOVT - if (offset < 0) { - ARMABI_MOVI2R(R0, (u32)(-offset)); - SUB(R0, gpr.R(rs), R0); - } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); - } - } - BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + if (gpr.IsImm(rs)) { + // We can compute the full address at compile time. Kickass. + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; + gpr.MapReg(rt, MAP_NOINIT | MAP_DIRTY); // must be OK even if rs == rt since we have the value from imm already. + ARMABI_MOVI2R(R0, addr); } else { - BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF + gpr.MapDirtyIn(rt, rs); + Operand2 op2; + if (offset) { + bool negated; + if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) { + if (!negated) + ADD(R0, gpr.R(rs), op2); + else + SUB(R0, gpr.R(rs), op2); + } else { + // Try to avoid using MOVT + if (offset < 0) { + ARMABI_MOVI2R(R0, (u32)(-offset)); + SUB(R0, gpr.R(rs), R0); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + } else { + BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF + } } if (o == 35) { LDR(gpr.R(rt), R11, R0, true, true); @@ -91,24 +102,35 @@ namespace MIPSComp case 40: //sb case 43: //WriteMem32(addr, R(rt)); break; //sw if (g_Config.bFastMemory) { - gpr.MapInIn(rt, rs); - Operand2 op2; - if (offset) { - if (TryMakeOperand2(offset, op2)) { - ADD(R0, gpr.R(rs), op2); - } else { - // Try to avoid using MOVT - if (offset < 0) { - ARMABI_MOVI2R(R0, (u32)(-offset)); - SUB(R0, gpr.R(rs), R0); - } else { - ARMABI_MOVI2R(R0, (u32)offset); - ADD(R0, gpr.R(rs), R0); - } - } - BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + if (gpr.IsImm(rs)) { + // We can compute the full address at compile time. Kickass. + u32 addr = (offset + gpr.GetImm(rs)) & 0x3FFFFFFF; + gpr.MapReg(rt); + ARMABI_MOVI2R(R0, addr); } else { - BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF + gpr.MapInIn(rt, rs); + Operand2 op2; + if (offset) { + bool negated; + if (TryMakeOperand2_AllowNegation(offset, op2, &negated)) { + if (!negated) + ADD(R0, gpr.R(rs), op2); + else + SUB(R0, gpr.R(rs), op2); + } else { + // Try to avoid using MOVT + if (offset < 0) { + ARMABI_MOVI2R(R0, (u32)(-offset)); + SUB(R0, gpr.R(rs), R0); + } else { + ARMABI_MOVI2R(R0, (u32)offset); + ADD(R0, gpr.R(rs), R0); + } + } + BIC(R0, R0, Operand2(0xC0, 4)); // &= 0x3FFFFFFF + } else { + BIC(R0, gpr.R(rs), Operand2(0xC0, 4)); // &= 0x3FFFFFFF + } } if (o == 43) { STR(R0, gpr.R(rt), R11, true, true); From b4af05a23c00c383b8b4b3a0c61385deda518d63 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Fri, 11 Jan 2013 23:46:56 +0100 Subject: [PATCH 41/49] Call init on drawbuffer. upgrade native. --- android/jni/NativeApp.cpp | 6 ++++++ native | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index ebfb7d2e0e..d250845e82 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -298,6 +298,9 @@ void NativeInitGraphics() theme.checkOn = I_CHECKEDBOX; theme.checkOff = I_SQUARE; + ui_draw2d.Init(); + ui_draw2d_front.Init(); + UIInit(&ui_atlas, theme); uiTexture = new Texture(); @@ -375,6 +378,9 @@ void NativeShutdownGraphics() delete screenManager; screenManager = 0; + ui_draw2d.Shutdown(); + ui_draw2d_front.Shutdown(); + UIShader_Shutdown(); gl_lost_manager_shutdown(); diff --git a/native b/native index af5142d32a..c60bda83b6 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit af5142d32a283ffc0ad1bba5d79a5f30fba717d9 +Subproject commit c60bda83b6871f151921ca818a4ba292714bd7ec From 674911ddbaccf36ca80e7158d963e6c7be98b1fc Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 12 Jan 2013 00:44:18 +0100 Subject: [PATCH 42/49] Move downcount into MIPSState for efficiency, enable block linking. On ARM JIT we can now reach it through the cpu context reg. --- Core/CoreTiming.cpp | 29 ++++++++++++------------ Core/CoreTiming.h | 1 - Core/MIPS/ARM/ArmCompALU.cpp | 20 +---------------- Core/MIPS/ARM/ArmJit.cpp | 12 +++++----- Core/MIPS/ARM/ArmJit.h | 3 ++- Core/MIPS/ARM/ArmJitCache.cpp | 42 +++++++++-------------------------- Core/MIPS/MIPS.cpp | 3 ++- Core/MIPS/MIPS.h | 7 ++++-- Core/MIPS/MIPSTables.cpp | 8 +++---- Core/MIPS/x86/Jit.cpp | 6 ++--- Core/MIPS/x86/Jit.h | 2 +- 11 files changed, 49 insertions(+), 84 deletions(-) diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 70a9431f5c..d3ec3c92f3 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -67,7 +67,9 @@ Event *eventPool = 0; Event *eventTsPool = 0; int allocatedTsEvents = 0; -int downcount, slicelength; +// Downcount has been moved to currentMIPS, to save a couple of clocks in every ARM JIT block +// as we can already reach that structure through a register. +int slicelength; s64 globalTimer; s64 idledCycles; @@ -153,7 +155,7 @@ void UnregisterAllEvents() void Init() { - downcount = INITIAL_SLICE_LENGTH; + currentMIPS->downcount = INITIAL_SLICE_LENGTH; slicelength = INITIAL_SLICE_LENGTH; globalTimer = 0; idledCycles = 0; @@ -183,7 +185,7 @@ void Shutdown() u64 GetTicks() { - return (u64)globalTimer + slicelength - downcount; + return (u64)globalTimer + slicelength - currentMIPS->downcount; } u64 GetIdleTicks() @@ -462,23 +464,23 @@ void MoveEvents() void Advance() { - int cyclesExecuted = slicelength - downcount; + int cyclesExecuted = slicelength - currentMIPS->downcount; globalTimer += cyclesExecuted; - downcount = slicelength; + currentMIPS->downcount = slicelength; ProcessFifoWaitEvents(); if (!first) { - // WARN_LOG(CPU, "WARNING - no events in queue. Setting downcount to 10000"); - downcount += 10000; + // WARN_LOG(CPU, "WARNING - no events in queue. Setting currentMIPS->downcount to 10000"); + currentMIPS->downcount += 10000; } else { slicelength = (int)(first->time - globalTimer); if (slicelength > MAX_SLICE_LENGTH) slicelength = MAX_SLICE_LENGTH; - downcount = slicelength; + currentMIPS->downcount = slicelength; } if (advanceCallback) advanceCallback(cyclesExecuted); @@ -496,13 +498,13 @@ void LogPendingEvents() void Idle(int maxIdle) { - int cyclesDown = downcount; + int cyclesDown = currentMIPS->downcount; if (maxIdle != 0 && cyclesDown > maxIdle) cyclesDown = maxIdle; if (first && cyclesDown > 0) { - int cyclesExecuted = slicelength - downcount; + int cyclesExecuted = slicelength - currentMIPS->downcount; int cyclesNextEvent = (int) (first->time - globalTimer); if (cyclesNextEvent < cyclesExecuted + cyclesDown) @@ -517,9 +519,9 @@ void Idle(int maxIdle) DEBUG_LOG(CPU, "Idle for %i cycles! (%f ms)", cyclesDown, cyclesDown / (float)(CPU_HZ * 0.001f)); idledCycles += cyclesDown; - downcount -= cyclesDown; - if (downcount == 0) - downcount = -1; + currentMIPS->downcount -= cyclesDown; + if (currentMIPS->downcount == 0) + currentMIPS->downcount = -1; } std::string GetScheduledEventsSummary() @@ -561,7 +563,6 @@ void DoState(PointerWrap &p) p.DoLinkedList(tsFirst, &tsLast); p.Do(CPU_HZ); - p.Do(downcount); p.Do(slicelength); p.Do(globalTimer); p.Do(idledCycles); diff --git a/Core/CoreTiming.h b/Core/CoreTiming.h index bed8aa7a46..0858982683 100644 --- a/Core/CoreTiming.h +++ b/Core/CoreTiming.h @@ -119,7 +119,6 @@ namespace CoreTiming void SetClockFrequencyMHz(int cpuMhz); int GetClockFrequencyMHz(); - extern int downcount; extern int slicelength; }; // end of namespace diff --git a/Core/MIPS/ARM/ArmCompALU.cpp b/Core/MIPS/ARM/ArmCompALU.cpp index e2e96f0456..a9b01bddf9 100644 --- a/Core/MIPS/ARM/ArmCompALU.cpp +++ b/Core/MIPS/ARM/ArmCompALU.cpp @@ -138,25 +138,7 @@ namespace MIPSComp break; } } - - //rd = rs X rt - /* - void Jit::CompTriArith(u32 op, void (XEmitter::*arith)(int, const OpArg &, const OpArg &)) - { - int rt = _RT; - int rs = _RS; - int rd = _RD; - - gpr.Lock(rt, rs, rd); - MOV(32, R(EAX), gpr.R(rs)); - MOV(32, R(EBX), gpr.R(rt)); - gpr.BindToRegister(rd, true, true); - (this->*arith)(32, R(EAX), R(EBX)); - MOV(32, gpr.R(rd), R(EAX)); - gpr.UnlockAll(); - } - */ - + void Jit::Comp_RType3(u32 op) { int rt = _RT; diff --git a/Core/MIPS/ARM/ArmJit.cpp b/Core/MIPS/ARM/ArmJit.cpp index 158b4179f2..cb0373ba43 100644 --- a/Core/MIPS/ARM/ArmJit.cpp +++ b/Core/MIPS/ARM/ArmJit.cpp @@ -205,18 +205,18 @@ void Jit::MovToPC(ARMReg r) { void Jit::DoDownCount() { - ARMABI_MOVI2R(R0, (u32)&CoreTiming::downcount); - LDR(R1, R0); - if(js.downcountAmount < 255) // We can enlarge this if we used rotations + LDR(R1, R10, offsetof(MIPSState, downcount)); + Operand2 op2; + if (TryMakeOperand2(js.downcountAmount, op2)) // We can enlarge this if we used rotations { - SUBS(R1, R1, js.downcountAmount); - STR(R0, R1); + SUBS(R1, R1, op2); + STR(R10, R1, offsetof(MIPSState, downcount)); } else { // Should be fine to use R2 here, flushed the regcache anyway. // If js.downcountAmount can be expressed as an Imm8, we don't need this anyway. ARMABI_MOVI2R(R2, js.downcountAmount); SUBS(R1, R1, R2); - STR(R0, R1); + STR(R10, R1, offsetof(MIPSState, downcount)); } } diff --git a/Core/MIPS/ARM/ArmJit.h b/Core/MIPS/ARM/ArmJit.h index 205e9c119d..036d71dc0b 100644 --- a/Core/MIPS/ARM/ArmJit.h +++ b/Core/MIPS/ARM/ArmJit.h @@ -31,7 +31,7 @@ struct ArmJitOptions { ArmJitOptions() { - enableBlocklink = false; + enableBlocklink = true; } bool enableBlocklink; @@ -131,6 +131,7 @@ private: MIPSState *mips_; +public: // Code pointers const u8 *enterCode; diff --git a/Core/MIPS/ARM/ArmJitCache.cpp b/Core/MIPS/ARM/ArmJitCache.cpp index ae08f2a37d..5f6769db04 100644 --- a/Core/MIPS/ARM/ArmJitCache.cpp +++ b/Core/MIPS/ARM/ArmJitCache.cpp @@ -95,8 +95,7 @@ ArmJitBlockCache::~ArmJitBlockCache() Shutdown(); } - -// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache +// This clears the JIT block cache. It's called from JitCache.cpp when the JIT cache // is full and when saving and loading states. void ArmJitBlockCache::Clear() { @@ -117,17 +116,6 @@ void ArmJitBlockCache::ClearSafe() #endif } -/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag) -{ - for (int i = 0; i < num_blocks; i++) - { - if (blocks[i].flags & death_flag) - { - DestroyBlock(i, false); - } - } -}*/ - void ArmJitBlockCache::Reset() { Shutdown(); @@ -279,13 +267,8 @@ std::string ArmJitBlockCache::GetCompiledDisassembly(int block_num) } - -//Block linker //Make sure to have as many blocks as possible compiled before calling this -//It's O(N), so it's fast :) -//Can be faster by doing a queue for blocks to link up, and only process those -//Should probably be done - +//It's O(1), so it's fast :) void ArmJitBlockCache::LinkBlockExits(int i) { ArmJitBlock &b = blocks[i]; @@ -302,7 +285,7 @@ void ArmJitBlockCache::LinkBlockExits(int i) if (destinationBlock != -1) { ARMXEmitter emit(b.exitPtrs[e]); - // emit.JMP(blocks[destinationBlock].checkedEntry, true); + emit.B(blocks[destinationBlock].checkedEntry); b.linkStatus[e] = true; } } @@ -366,25 +349,20 @@ void ArmJitBlockCache::DestroyBlock(int block_num, bool invalidate) UnlinkBlock(block_num); + blockCodePointers[block_num] = 0; // Send anyone who tries to run this block back to the dispatcher. // Not entirely ideal, but .. pretty good. - // Spurious entrances from previously linked blocks can only come through checkedEntry + // I hope there's enough space... + // checkedEntry is the only "linked" entrance so it's enough to overwrite that. ARMXEmitter emit((u8 *)b.checkedEntry); - //emit.MOV(32, M(&mips->pc), Imm32(b.originalAddress)); - //emit.JMP(MIPSComp::jit->Asm().dispatcher, true); - - // this is not needed really - /* - emit.SetCodePtr((u8 *)blockCodePointers[blocknum]); - emit.MOV(32, M(&PC), Imm32(b.originalAddress)); - emit.JMP(asm_routines.dispatcher, true); - */ + emit.ARMABI_MOVI2R(R0, b.originalAddress); + emit.STR(R10, R0, offsetof(MIPSState, pc)); + emit.B(MIPSComp::jit->dispatcher); } void ArmJitBlockCache::InvalidateICache(u32 address, const u32 length) { - // Convert the logical address to a physical address for the block map - u32 pAddr = address & 0x1FFFFFFF; + u32 pAddr = address & 0x3FFFFFFF; // destroy JIT blocks // !! this works correctly under assumption that any two overlapping blocks end at the same address diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index b7d89cd873..f7a3b5d036 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -110,6 +110,7 @@ void MIPSState::DoState(PointerWrap &p) p.DoArray(vfpuWriteMask, sizeof(vfpuWriteMask) / sizeof(vfpuWriteMask[0])); p.Do(pc); p.Do(nextPC); + p.Do(downcount); p.Do(hi); p.Do(lo); p.Do(fpcond); @@ -131,7 +132,7 @@ void MIPSState::SetWriteMask(const bool wm[4]) void MIPSState::SingleStep() { int cycles = MIPS_SingleStep(); - CoreTiming::downcount -= cycles; + currentMIPS->downcount -= cycles; CoreTiming::Advance(); } diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 701c1afdbc..0bf9c6c529 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -122,6 +122,8 @@ public: u32 pc; u32 nextPC; + u32 downcount; // This really doesn't belong here, it belongs in CoreTiming. But you gotta do what you gotta do, this needs to be reachable in the ARM JIT. + u32 hi; u32 lo; @@ -129,11 +131,12 @@ public: u32 fcr31; //fpu control register u32 fpcond; // cache the cond flag of fcr31 (& 1 << 23) - GMRng rng; // VFPU hardware random number generator. Probably not the right type. - bool inDelaySlot; int llBit; // ll/sc + + GMRng rng; // VFPU hardware random number generator. Probably not the right type. + // Debug stuff u32 debugCount; // can be used to count basic blocks before crashes, etc. diff --git a/Core/MIPS/MIPSTables.cpp b/Core/MIPS/MIPSTables.cpp index 1b5807ac3c..81657fe434 100644 --- a/Core/MIPS/MIPSTables.cpp +++ b/Core/MIPS/MIPSTables.cpp @@ -948,7 +948,7 @@ int MIPSInterpret_RunUntil(u64 globalTicks) while (coreState == CORE_RUNNING) { // NEVER stop in a delay slot! - while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) + while (curMips->downcount >= 0 && coreState == CORE_RUNNING) { // int cycles = 0; { @@ -990,7 +990,7 @@ int MIPSInterpret_RunUntil(u64 globalTicks) curMips->pc = curMips->nextPC; curMips->inDelaySlot = false; } - CoreTiming::downcount -= 1; + curMips->downcount -= 1; goto again; } } @@ -1025,7 +1025,7 @@ int MIPSInterpret_RunFastUntil(u64 globalTicks) MIPSState *curMips = currentMIPS; while (coreState == CORE_RUNNING) { - while (CoreTiming::downcount >= 0 && coreState == CORE_RUNNING) // TODO: Try to get rid of the latter check + while (curMips->downcount >= 0 && coreState == CORE_RUNNING) // TODO: Try to get rid of the latter check { again: bool wasInDelaySlot = curMips->inDelaySlot; @@ -1122,7 +1122,7 @@ int MIPSInterpret_RunFastUntil(u64 globalTicks) curMips->pc = curMips->nextPC; curMips->inDelaySlot = false; } - CoreTiming::downcount -= 1; + curMips->downcount -= 1; goto again; } } diff --git a/Core/MIPS/x86/Jit.cpp b/Core/MIPS/x86/Jit.cpp index 90553222a4..f5149f1264 100644 --- a/Core/MIPS/x86/Jit.cpp +++ b/Core/MIPS/x86/Jit.cpp @@ -137,7 +137,7 @@ void Jit::Comp_Generic(u32 op) void Jit::WriteExit(u32 destination, int exit_num) { - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + SUB(32, M(¤tMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); //If nobody has taken care of this yet (this can be removed when all branches are done) JitBlock *b = js.curBlock; @@ -161,13 +161,13 @@ void Jit::WriteExitDestInEAX() { // TODO: Some wasted potential, dispatcher will alwa MOV(32, M(&mips_->pc), R(EAX)); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + SUB(32, M(¤tMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); JMP(asm_.dispatcher, true); } void Jit::WriteSyscallExit() { - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + SUB(32, M(¤tMIPS->downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); JMP(asm_.dispatcherCheckCoreState, true); } diff --git a/Core/MIPS/x86/Jit.h b/Core/MIPS/x86/Jit.h index cd5a511efc..9f3281830f 100644 --- a/Core/MIPS/x86/Jit.h +++ b/Core/MIPS/x86/Jit.h @@ -35,7 +35,7 @@ struct JitOptions { JitOptions() { - enableBlocklink = false; + enableBlocklink = true; } bool enableBlocklink; From 64f953c5e17f326b051c859ec570d87769509e18 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Sat, 12 Jan 2013 12:18:02 +0100 Subject: [PATCH 43/49] 8-byte align the ARM stack. Type some ideas. --- Core/MIPS/ARM/ArmAsm.cpp | 26 +++++++++++++++++++++++--- Core/MIPS/ARM/ArmCompLoadStore.cpp | 26 ++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index 12cf336764..d089410cd7 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -65,13 +65,22 @@ void JitAt() MIPSComp::jit->Compile(currentMIPS->pc); } +/* +double testD; + +u32 DoubleTest(u32 sp) { + volatile double local = 1.0; + testD += local; + return (u32)(&local); +} + void ShowPC(u32 sp) { if (currentMIPS) { - WARN_LOG(HLE, "PC : %08x ArmSP : %08x", currentMIPS->pc, sp); + ERROR_LOG(HLE, "ShowPC : %08x ArmSP : %08x", currentMIPS->pc, sp); } else { ERROR_LOG(HLE, "Universe corrupt?"); } -} +}*/ void DisassembleArm(const u8 *data, int size); @@ -90,6 +99,13 @@ void Jit::GenerateFixedCode() SetCC(CC_AL); PUSH(9, R4, R5, R6, R7, R8, R9, R10, R11, _LR); + // Take care to 8-byte align stack for function calls. + // This actually misaligns the stack within the JIT itself but that doesn't really matter + // as the JIT does not use the stack at all. + SUB(_SP, _SP, 4); + + // QuickCallFunction(R3, (void *)&DoubleTest); + // QuickCallFunction(R3, (void *)&ShowPC); // Fixed registers, these are always kept when in Jit context. // R13 cannot be used as it's the stack pointer. @@ -132,7 +148,9 @@ void Jit::GenerateFixedCode() BIC(R0, R0, Operand2(0xFC, 4)); CMP(R1, Operand2(MIPS_EMUHACK_OPCODE >> 24, 4)); SetCC(CC_EQ); - // IDEA - we have 24 bits, why not just use offsets from base of code? + // 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, true, true); here, replacing the two next instructions. ADD(R0, R9, Operand2(2, ST_LSL, R0)); LDR(R0, R0); B(R0); @@ -154,6 +172,8 @@ void Jit::GenerateFixedCode() breakpointBailout = GetCodePtr(); + ADD(_SP, _SP, 4); + POP(9, R4, R5, R6, R7, R8, R9, R10, R11, _PC); // Returns // Uncomment if you want to see the output... diff --git a/Core/MIPS/ARM/ArmCompLoadStore.cpp b/Core/MIPS/ARM/ArmCompLoadStore.cpp index 5479580c98..b310b89363 100644 --- a/Core/MIPS/ARM/ArmCompLoadStore.cpp +++ b/Core/MIPS/ARM/ArmCompLoadStore.cpp @@ -14,13 +14,35 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + + +// Optimization ideas: +// +// It's common to see sequences of stores writing or reading to a contiguous set of +// addresses in function prologues/epilogues: +// sw s5, 104(sp) +// sw s4, 100(sp) +// sw s3, 96(sp) +// sw s2, 92(sp) +// sw s1, 88(sp) +// sw s0, 84(sp) +// sw ra, 108(sp) +// mov s4, a0 +// mov s3, a1 +// ... +// Such sequences could easily be detected and turned into nice contiguous +// sequences of ARM stores instead of the current 3 instructions per sw/lw. +// +// Also, if we kept track of the likely register content of a cached register, +// (pointer or data), we could avoid many BIC instructions. + + #include "../../MemMap.h" #include "../MIPSAnalyst.h" #include "../../Config.h" #include "ArmJit.h" #include "ArmRegCache.h" - #define _RS ((op>>21) & 0x1F) #define _RT ((op>>16) & 0x1F) #define _RD ((op>>11) & 0x1F) @@ -44,7 +66,7 @@ namespace MIPSComp // Don't load anything into $zr return; } - switch (o) + switch (o) { case 37: //R(rt) = ReadMem16(addr); break; //lhu Comp_Generic(op); From 892169e21587700662454e34948e0340cc400284 Mon Sep 17 00:00:00 2001 From: Sacha Date: Sun, 13 Jan 2013 20:52:41 +1000 Subject: [PATCH 44/49] Update project files for changes in armjit. CMake and Qt. --- CMakeLists.txt | 31 ++++++++++++++++--------------- Qt/Core.pro | 27 ++++++++++++++------------- 2 files changed, 30 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 860e4e8b72..0387125c7b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,8 +177,6 @@ elseif(X86) Common/ABI.h Common/CPUDetect.cpp Common/CPUDetect.h - Common/MathUtil.cpp - Common/MathUtil.h Common/Thunk.cpp Common/Thunk.h Common/x64Analyzer.cpp @@ -220,6 +218,8 @@ add_library(Common STATIC Common/IniFile.h Common/LogManager.cpp Common/LogManager.h + Common/MathUtil.cpp + Common/MathUtil.h Common/MemArena.cpp Common/MemArena.h Common/MemoryUtil.cpp @@ -579,19 +579,20 @@ include_directories(ext/libkirk) set(CoreExtra) if(ARM) set(CoreExtra ${CoreExtra} - Core/MIPS/ARM/Asm.cpp - Core/MIPS/ARM/Asm.h - Core/MIPS/ARM/CompALU.cpp - Core/MIPS/ARM/CompBranch.cpp - Core/MIPS/ARM/CompFPU.cpp - Core/MIPS/ARM/CompLoadStore.cpp - Core/MIPS/ARM/CompVFPU.cpp - Core/MIPS/ARM/Jit.cpp - Core/MIPS/ARM/Jit.h - Core/MIPS/ARM/JitCache.cpp - Core/MIPS/ARM/JitCache.h - Core/MIPS/ARM/RegCache.cpp - Core/MIPS/ARM/RegCache.h) + Core/MIPS/ARM/ArmAsm.cpp + Core/MIPS/ARM/ArmAsm.h + Core/MIPS/ARM/ArmCompALU.cpp + Core/MIPS/ARM/ArmCompBranch.cpp + Core/MIPS/ARM/ArmCompFPU.cpp + Core/MIPS/ARM/ArmCompLoadStore.cpp + Core/MIPS/ARM/ArmCompVFPU.cpp + Core/MIPS/ARM/ArmJit.cpp + Core/MIPS/ARM/ArmJit.h + Core/MIPS/ARM/ArmJitCache.cpp + Core/MIPS/ARM/ArmJitCache.h + Core/MIPS/ARM/ArmRegCache.cpp + Core/MIPS/ARM/ArmRegCache.h + ext/disarm.cpp) elseif(X86) set(CoreExtra ${CoreExtra} Core/MIPS/x86/Asm.cpp diff --git a/Qt/Core.pro b/Qt/Core.pro index 67e81ff4ef..cb36bc09a4 100755 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -9,20 +9,21 @@ include(Settings.pri) INCLUDEPATH += ../native ../Common ../ arm { - SOURCES += ../Core/MIPS/ARM/Asm.cpp \ #CoreARM - ../Core/MIPS/ARM/CompALU.cpp \ - ../Core/MIPS/ARM/CompBranch.cpp \ - ../Core/MIPS/ARM/CompFPU.cpp \ - ../Core/MIPS/ARM/CompLoadStore.cpp \ - ../Core/MIPS/ARM/CompVFPU.cpp \ - ../Core/MIPS/ARM/Jit.cpp \ - ../Core/MIPS/ARM/JitCache.cpp \ - ../Core/MIPS/ARM/RegCache.cpp + SOURCES += ../Core/MIPS/ARM/ArmAsm.cpp \ #CoreARM + ../Core/MIPS/ARM/ArmCompALU.cpp \ + ../Core/MIPS/ARM/ArmCompBranch.cpp \ + ../Core/MIPS/ARM/ArmCompFPU.cpp \ + ../Core/MIPS/ARM/ArmCompLoadStore.cpp \ + ../Core/MIPS/ARM/ArmCompVFPU.cpp \ + ../Core/MIPS/ARM/ArmJit.cpp \ + ../Core/MIPS/ARM/ArmJitCache.cpp \ + ../Core/MIPS/ARM/ArmRegCache.cpp \ + ../ext/disarm.cpp - HEADERS += ../Core/MIPS/ARM/Asm.h \ - ../Core/MIPS/ARM/Jit.h \ - ../Core/MIPS/ARM/JitCache.h \ - ../Core/MIPS/ARM/RegCache.h + HEADERS += ../Core/MIPS/ARM/ArmAsm.h \ + ../Core/MIPS/ARM/ArmJit.h \ + ../Core/MIPS/ARM/ArmJitCache.h \ + ../Core/MIPS/ARM/ArmRegCache.h } x86 { SOURCES += ../Core/MIPS/x86/Asm.cpp \ From 66ee2e2933eb95c7ef2782f14b3427b99f27acfa Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 14 Jan 2013 22:19:18 +0100 Subject: [PATCH 45/49] Remove ArmABI.cpp/h, didn't need that stuff. --- CMakeLists.txt | 2 -- Common/ArmEmitter.h | 7 ------- Common/Common.vcxproj | 12 ------------ Common/Common.vcxproj.filters | 2 -- Core/MIPS/ARM/ArmAsm.cpp | 6 +----- Core/MIPS/ARM/ArmRegCache.cpp | 1 - Core/MIPS/ARM/ArmRegCache.h | 1 - android/jni/Android.mk | 1 - android/jni/ArmEmitterTest.cpp | 8 +++----- 9 files changed, 4 insertions(+), 36 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 935be34623..113baded6c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -169,8 +169,6 @@ endmacro() set(CommonExtra) if(ARM) set(CommonExtra ${CommonExtra} - Common/ArmABI.h - Common/ArmABI.cpp Common/ArmCPUDetect.cpp Common/ArmEmitter.h Common/ArmEmitter.cpp diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 0819bcb2e1..87e17448f9 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -475,13 +475,6 @@ public: void QuickCallFunction(ARMReg scratchreg, void *func); // Utility functions - void ARMABI_CallFunction(void *func); - void ARMABI_CallFunctionC(void *func, u32 Arg0); - void ARMABI_CallFunctionCNoSave(void *func, u32 Arg0); - void ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2); - void ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3); - void ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); - void ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); void ARMABI_MOVI2R(ARMReg reg, u32 val); void ARMABI_ShowConditions(); void ARMABI_Return(); diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index 8defc7f277..4c8a256ba4 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -140,12 +140,6 @@ - - true - true - true - true - true true @@ -195,12 +189,6 @@ - - true - true - true - true - true true diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index dd3b8e0ac9..3e2c3b50bc 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -42,7 +42,6 @@ - @@ -70,7 +69,6 @@ - diff --git a/Core/MIPS/ARM/ArmAsm.cpp b/Core/MIPS/ARM/ArmAsm.cpp index d089410cd7..c8cfcaca64 100644 --- a/Core/MIPS/ARM/ArmAsm.cpp +++ b/Core/MIPS/ARM/ArmAsm.cpp @@ -16,7 +16,6 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "ArmEmitter.h" -#include "ArmABI.h" #include "../../MemMap.h" @@ -104,9 +103,6 @@ void Jit::GenerateFixedCode() // as the JIT does not use the stack at all. SUB(_SP, _SP, 4); - // QuickCallFunction(R3, (void *)&DoubleTest); - // QuickCallFunction(R3, (void *)&ShowPC); - // Fixed registers, these are always kept when in Jit context. // R13 cannot be used as it's the stack pointer. ARMABI_MOVI2R(R11, (u32)Memory::base); @@ -157,7 +153,7 @@ void Jit::GenerateFixedCode() SetCC(CC_AL); //Ok, no block, let's jit - ARMABI_CallFunction((void *)&JitAt); + QuickCallFunction(R2, (void *)&JitAt); B(dispatcherNoCheck); // no point in special casing this diff --git a/Core/MIPS/ARM/ArmRegCache.cpp b/Core/MIPS/ARM/ArmRegCache.cpp index 277fbb33b0..3b9122bb18 100644 --- a/Core/MIPS/ARM/ArmRegCache.cpp +++ b/Core/MIPS/ARM/ArmRegCache.cpp @@ -15,7 +15,6 @@ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ -#include "ArmABI.h" #include "ArmRegCache.h" #include "ArmEmitter.h" diff --git a/Core/MIPS/ARM/ArmRegCache.h b/Core/MIPS/ARM/ArmRegCache.h index 691bb9d0c7..e6adada891 100644 --- a/Core/MIPS/ARM/ArmRegCache.h +++ b/Core/MIPS/ARM/ArmRegCache.h @@ -20,7 +20,6 @@ #include "ArmEmitter.h" #include "../MIPS.h" #include "../MIPSAnalyst.h" -#include "ArmABI.h" using namespace ArmGen; diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 90c0e9827c..3e3356e0d8 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -57,7 +57,6 @@ LOCAL_SRC_FILES := \ $(SRC)/ext/libkirk/kirk_engine.c \ $(SRC)/ext/snappy/snappy-c.cpp \ $(SRC)/ext/snappy/snappy.cpp \ - $(SRC)/Common/ArmABI.cpp \ $(SRC)/Common/ArmEmitter.cpp \ $(SRC)/Common/ArmCPUDetect.cpp \ $(SRC)/Common/ArmThunk.cpp \ diff --git a/android/jni/ArmEmitterTest.cpp b/android/jni/ArmEmitterTest.cpp index 93cbb00d3b..a035bcba7b 100644 --- a/android/jni/ArmEmitterTest.cpp +++ b/android/jni/ArmEmitterTest.cpp @@ -1,7 +1,6 @@ #include "base/logging.h" #include "ArmEmitterTest.h" -#include "Common/ArmABI.h" #include "Common/ArmEmitter.h" static bool functionWasCalled; @@ -48,7 +47,7 @@ void TestCode::Generate() SetCC(CC_NEQ); MOV(R2, Operand2(0xFF, 0)); SetCC(); - ARMABI_CallFunction((void*)&TestLeaf); + QuickCallFunction(R3, (void*)&TestLeaf); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x1, 0x100, 0x1337); //ARMABI_CallFunctionCCC((void*)&TestLeaf, 0x2, 0x100, 0x31337); @@ -57,9 +56,7 @@ void TestCode::Generate() testCodePtr2 = this->GetCodePtr(); PUSH(2, R11, _LR); - ARMABI_PushAllCalleeSavedRegsAndAdjustStack(); - ARMABI_CallFunction((void*)&TestLeaf2); - ARMABI_PopAllCalleeSavedRegsAndAdjustStack(); + QuickCallFunction(R3, (void*)&TestLeaf2); POP(2, R11, _PC); } @@ -73,6 +70,7 @@ void CallPtr(const void *ptr) void ArmEmitterTest() { + ILOG("Running ARM emitter test!"); TestCode gen; gen.ReserveCodeSpace(0x4000); gen.Generate(); From 74a46f60ed7a87d6ae11ddcc493c757372b20eef Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 14 Jan 2013 23:03:23 +0100 Subject: [PATCH 46/49] Initialize downcount. --- Core/MIPS/MIPS.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index f7a3b5d036..b7c3a39323 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -93,6 +93,7 @@ void MIPSState::Reset() inDelaySlot = false; llBit = 0; nextPC = 0; + downcount = 0; // Initialize the VFPU random number generator with .. something? rng.Init(0x1337); } From 72333821bf2f62d4df4d451b4a625f77c029d0c1 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 14 Jan 2013 23:55:53 +0100 Subject: [PATCH 47/49] Disable ArmEmitterTest on startup, it seems to cause problems? --- android/jni/NativeApp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/jni/NativeApp.cpp b/android/jni/NativeApp.cpp index d6cec74d1d..fb575e1c2e 100644 --- a/android/jni/NativeApp.cpp +++ b/android/jni/NativeApp.cpp @@ -160,7 +160,7 @@ void NativeGetAppInfo(std::string *app_dir_name, std::string *app_nice_name, boo *app_dir_name = "ppsspp"; *landscape = true; - ArmEmitterTest(); + // ArmEmitterTest(); } void NativeInit(int argc, const char *argv[], const char *savegame_directory, const char *external_directory, const char *installID) From e34288070ced40479ee7a33328458e8a796b6676 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Tue, 15 Jan 2013 20:42:45 +0100 Subject: [PATCH 48/49] Don't flush the icache from the start, also don't sleep. May help JIT slowdowns. --- Common/ArmEmitter.cpp | 22 ++++++++++++++-------- Common/ArmEmitter.h | 13 +++++++++++-- Core/MIPS/ARM/ArmJitCache.cpp | 2 ++ 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Common/ArmEmitter.cpp b/Common/ArmEmitter.cpp index 92c24b63c2..a27d6e82ad 100644 --- a/Common/ArmEmitter.cpp +++ b/Common/ArmEmitter.cpp @@ -157,15 +157,21 @@ const u8 *ARMXEmitter::AlignCodePage() void ARMXEmitter::FlushIcache() { -#ifdef __SYMBIAN32__ - User::IMB_Range( startcode, code ); -#elif defined(BLACKBERRY) - msync(startcode, code-startcode, MS_SYNC | MS_INVALIDATE_ICACHE); -#else - __builtin___clear_cache (startcode, code); -#endif - SLEEP(0); + FlushIcacheSection(lastCacheFlushEnd, code); + lastCacheFlushEnd = code; } + +void ARMXEmitter::FlushIcacheSection(u8 *start, u8 *end) +{ +#ifdef __SYMBIAN32__ + User::IMB_Range( start, end); +#elif defined(BLACKBERRY) + msync(start, end - start, MS_SYNC | MS_INVALIDATE_ICACHE); +#else + __builtin___clear_cache (start, end); +#endif +} + void ARMXEmitter::SetCC(CCFlags cond) { condition = cond << 28; diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 87e17448f9..707d8b7717 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -331,6 +331,7 @@ class ARMXEmitter friend struct OpArg; // for Write8 etc private: u8 *code, *startcode; + u8 *lastCacheFlushEnd; u32 condition; void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2); @@ -346,8 +347,15 @@ protected: inline void Write32(u32 value) {*(u32*)code = value; code+=4;} public: - ARMXEmitter() { code = NULL; startcode = NULL; condition = CC_AL << 28;} - ARMXEmitter(u8 *code_ptr) { code = code_ptr; startcode = code_ptr; condition = CC_AL << 28;} + ARMXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) { + condition = CC_AL << 28; + } + ARMXEmitter(u8 *code_ptr) { + code = code_ptr; + lastCacheFlushEnd = code_ptr; + startcode = code_ptr; + condition = CC_AL << 28; + } virtual ~ARMXEmitter() {} void SetCodePtr(u8 *ptr); @@ -356,6 +364,7 @@ public: const u8 *AlignCodePage(); const u8 *GetCodePtr() const; void FlushIcache(); + void FlushIcacheSection(u8 *start, u8 *end); u8 *GetWritableCodePtr(); void SetCC(CCFlags cond = CC_AL); diff --git a/Core/MIPS/ARM/ArmJitCache.cpp b/Core/MIPS/ARM/ArmJitCache.cpp index 5f6769db04..c457e0c9c3 100644 --- a/Core/MIPS/ARM/ArmJitCache.cpp +++ b/Core/MIPS/ARM/ArmJitCache.cpp @@ -286,6 +286,7 @@ void ArmJitBlockCache::LinkBlockExits(int i) { ARMXEmitter emit(b.exitPtrs[e]); emit.B(blocks[destinationBlock].checkedEntry); + emit.FlushIcache(); b.linkStatus[e] = true; } } @@ -358,6 +359,7 @@ void ArmJitBlockCache::DestroyBlock(int block_num, bool invalidate) emit.ARMABI_MOVI2R(R0, b.originalAddress); emit.STR(R10, R0, offsetof(MIPSState, pc)); emit.B(MIPSComp::jit->dispatcher); + emit.FlushIcache(); } void ArmJitBlockCache::InvalidateICache(u32 address, const u32 length) From a85beb64f1c74bd0fba649c82622ab3e009365e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 15 Jan 2013 22:49:27 +0100 Subject: [PATCH 49/49] Buildfixes Mac --- CMakeLists.txt | 4 +++- Common/ArmEmitter.h | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 113baded6c..bd0e5eabdf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -915,8 +915,10 @@ set(NativeAppSource android/jni/MenuScreens.cpp android/jni/GamepadEmu.cpp android/jni/UIShader.cpp - android/jni/ArmEmitterTest.cpp android/jni/ui_atlas.cpp) +if(ANDROID) + set(NativeAppSource ${NativeAppSource} android/jni/ArmEmitterTest.cpp) +endif() set(NativeAssets android/assets/ui_atlas.zim assets/ppge_atlas.zim) diff --git a/Common/ArmEmitter.h b/Common/ArmEmitter.h index 707d8b7717..201b3b5c57 100644 --- a/Common/ArmEmitter.h +++ b/Common/ArmEmitter.h @@ -296,8 +296,6 @@ public: _assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8VFP not IMM"); return ((Value & 0xF0) << 12) | (Value & 0xF); } - - }; // Use these when you don't know if an imm can be represented as an operand2. @@ -308,14 +306,9 @@ bool TryMakeOperand2(u32 imm, Operand2 &op2); bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse); bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated); - inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); } inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); } -//usage: int a[]; ARRAY_OFFSET(a,10) -#define ARRAY_OFFSET(array,index) ((u32)((u64)&(array)[index]-(u64)&(array)[0])) -//usage: struct {int e;} s; STRUCT_OFFSET(s,e) -#define STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str))) struct FixupBranch {