diff --git a/AGENTS.md b/AGENTS.md index 3e8d5571ee..3947f579f5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,6 +77,9 @@ for it: the next test. Concurrent runs collide in PSPLink and `host0:`: they time out, their results are garbage, and the PSP wedges until someone resets it by hand. This has happened repeatedly; there is no case where it is acceptable. +8. **Update submodules when you start working.** Run `git submodule update --init --recursive` + before anything else, and again after switching or rebasing branches, so that builds and tests + use the submodule commits the branch records. ## Core Safety Checks diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index f269ad4689..e47554fb94 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -337,7 +337,6 @@ add_library(Common STATIC CommonTypes.h CommonWindows.h DbgNew.h - FakeEmitter.h FakeCPUDetect.cpp ExceptionHandlerSetup.cpp ExceptionHandlerSetup.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index ff1434abb5..42db8c9207 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -414,7 +414,6 @@ - diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index 9a02b190d8..50a14be6e1 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -54,7 +54,6 @@ Serialize - ext\libpng17 diff --git a/Common/FakeEmitter.h b/Common/FakeEmitter.h deleted file mode 100644 index 5727be7b5c..0000000000 --- a/Common/FakeEmitter.h +++ /dev/null @@ -1,425 +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/ - -// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!! - -#ifndef _DOLPHIN_FAKE_CODEGEN_ -#define _DOLPHIN_FAKE_CODEGEN_ - -#include - -#include "Common/CommonTypes.h" -#include "Common/CodeBlock.h" - -// VCVT flags -#define TO_FLOAT 0 -#define TO_INT 1 << 0 -#define IS_SIGNED 1 << 1 -#define ROUND_TO_ZERO 1 << 2 - -namespace FakeGen -{ -enum FakeReg -{ - // GPRs - R0 = 0, R1, R2, R3, R4, R5, - R6, R7, R8, R9, R10, R11, - - // SPRs - // R13 - R15 are SP, LR, and PC. - // Almost always referred to by name instead of register number - R12 = 12, R13 = 13, R14 = 14, R15 = 15, - R_IP = 12, R_SP = 13, R_LR = 14, R_PC = 15, - - - // VFP single precision registers - 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, 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, - - // for NEON VLD/VST instructions - REG_UPDATE = R13, - INVALID_REG = 0xFFFFFFFF -}; - -enum CCFlags -{ - CC_EQ = 0, // Equal - CC_NEQ, // Not equal - CC_CS, // Carry Set - CC_CC, // Carry Clear - CC_MI, // Minus (Negative) - CC_PL, // Plus - CC_VS, // Overflow - CC_VC, // No Overflow - CC_HI, // Unsigned higher - CC_LS, // Unsigned lower or same - CC_GE, // Signed greater than or equal - CC_LT, // Signed less than - CC_GT, // Signed greater than - CC_LE, // Signed less than or equal - CC_AL, // Always (unconditional) 14 - 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; - -enum ShiftType -{ - ST_LSL = 0, - ST_ASL = 0, - ST_LSR = 1, - ST_ASR = 2, - ST_ROR = 3, - ST_RRX = 4 -}; -enum IntegerSize -{ - I_I8 = 0, - I_I16, - I_I32, - I_I64 -}; - -enum -{ - NUMGPRs = 13, -}; - -class FakeXEmitter; - -enum OpType -{ - TYPE_IMM = 0, - TYPE_REG, - TYPE_IMMSREG, - TYPE_RSR, - TYPE_MEM -}; - -// This is no longer a proper operand2 class. Need to split up. -class Operand2 -{ - friend class FakeXEmitter; -protected: - u32 Value; - -private: - OpType Type; - - // IMM types - u8 Rotation; // Only for u8 values - - // Register types - u8 IndexOrShift; - ShiftType Shift; -public: - OpType GetType() - { - return Type; - } - Operand2() {} - Operand2(u32 imm, OpType type = TYPE_IMM) : IndexOrShift(), Shift() - { - Type = type; - Value = imm; - Rotation = 0; - } - - Operand2(FakeReg Reg) : IndexOrShift(), Shift() - { - Type = TYPE_REG; - Value = Reg; - Rotation = 0; - } - Operand2(u8 imm, u8 rotation) : IndexOrShift(), Shift() - { - Type = TYPE_IMM; - Value = imm; - Rotation = rotation; - } - Operand2(FakeReg base, ShiftType type, FakeReg shift) : Rotation(0) // RSR - { - Type = TYPE_RSR; - _assert_msg_(type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); - IndexOrShift = shift; - Shift = type; - Value = base; - } - - Operand2(FakeReg base, ShiftType type, u8 shift) : Rotation(0) // For IMM shifted register - { - if(shift == 32) shift = 0; - switch (type) - { - case ST_LSL: - _assert_msg_(shift < 32, "Invalid Operand2: LSL %u", shift); - break; - case ST_LSR: - _assert_msg_(shift <= 32, "Invalid Operand2: LSR %u", shift); - if (!shift) - type = ST_LSL; - if (shift == 32) - shift = 0; - break; - case ST_ASR: - _assert_msg_(shift < 32, "Invalid Operand2: ASR %u", shift); - if (!shift) - type = ST_LSL; - if (shift == 32) - shift = 0; - break; - case ST_ROR: - _assert_msg_(shift < 32, "Invalid Operand2: ROR %u", shift); - if (!shift) - type = ST_LSL; - break; - case ST_RRX: - _assert_msg_(shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); - type = ST_ROR; - break; - } - IndexOrShift = shift; - Shift = type; - Value = base; - Type = TYPE_IMMSREG; - } - u32 GetData() - { - switch(Type) - { - case TYPE_IMM: - return Imm12Mod(); // This'll need to be changed later - case TYPE_REG: - return Rm(); - case TYPE_IMMSREG: - return IMMSR(); - case TYPE_RSR: - return RSR(); - default: - _assert_msg_(false, "GetData with Invalid Type"); - return 0; - } - } - u32 IMMSR() // IMM shifted register - { - _assert_msg_(Type == TYPE_IMMSREG, "IMMSR must be imm shifted register"); - return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value); - } - u32 RSR() // Register shifted register - { - _assert_msg_(Type == TYPE_RSR, "RSR must be RSR Of Course"); - return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value; - } - u32 Rm() - { - _assert_msg_(Type == TYPE_REG, "Rm must be with Reg"); - return Value; - } - - u32 Imm5() - { - _assert_msg_(Type == TYPE_IMM, "Imm5 not IMM value"); - return ((Value & 0x0000001F) << 7); - } - u32 Imm8() - { - _assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value"); - return Value & 0xFF; - } - u32 Imm8Rot() // IMM8 with Rotation - { - _assert_msg_(Type == TYPE_IMM, "Imm8Rot not IMM value"); - _assert_msg_((Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation); - return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF); - } - u32 Imm12() - { - _assert_msg_(Type == TYPE_IMM, "Imm12 not IMM"); - return (Value & 0x00000FFF); - } - - u32 Imm12Mod() - { - // This is an IMM12 with the top four bits being rotation and the - // bottom eight being an IMM. This is for instructions that need to - // expand a 8bit IMM to a 32bit value and gives you some rotation as - // well. - // Each rotation rotates to the right by 2 bits - _assert_msg_(Type == TYPE_IMM, "Imm12Mod not IMM"); - return ((Rotation & 0xF) << 8) | (Value & 0xFF); - } - u32 Imm16() - { - _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); - return ( (Value & 0xF000) << 4) | (Value & 0x0FFF); - } - u32 Imm16Low() - { - return Imm16(); - } - u32 Imm16High() // Returns high 16bits - { - _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); - return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF); - } - u32 Imm24() - { - _assert_msg_(Type == TYPE_IMM, "Imm16 not IMM"); - return (Value & 0x0FFFFFFF); - } -}; - -// 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); - -// Use this only when you know imm can be made into an Operand2. -Operand2 AssumeMakeOperand2(u32 imm); - -inline Operand2 R(FakeReg Reg) { return Operand2(Reg, TYPE_REG); } -inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } -inline Operand2 Mem(void *ptr) { return Operand2((u32)(uintptr_t)ptr, TYPE_IMM); } -//usage: struct {int e;} s; STRUCT_OFFSET(s,e) -#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str))) - - -struct FixupBranch -{ - u8 *ptr; - u32 condition; // Remembers our codition at the time - int type; //0 = B 1 = BL -}; - -typedef const u8* JumpTarget; - -// XXX: Stop polluting the global namespace -const u32 I_8 = (1 << 0); -const u32 I_16 = (1 << 1); -const u32 I_32 = (1 << 2); -const u32 I_64 = (1 << 3); -const u32 I_SIGNED = (1 << 4); -const u32 I_UNSIGNED = (1 << 5); -const u32 F_32 = (1 << 6); -const u32 I_POLYNOMIAL = (1 << 7); // Only used in VMUL/VMULL - -u32 EncodeVd(FakeReg Vd); -u32 EncodeVn(FakeReg Vn); -u32 EncodeVm(FakeReg Vm); - -u32 encodedSize(u32 value); - -// Subtracts the base from the register to give us the real one -FakeReg SubBase(FakeReg Reg); - -// See A.7.1 in the Fakev7-A -// VMUL F32 scalars can only be up to D15[0], D15[1] - higher scalars cannot be individually addressed -FakeReg DScalar(FakeReg dreg, int subScalar); -FakeReg QScalar(FakeReg qreg, int subScalar); - -enum NEONAlignment { - ALIGN_NONE = 0, - ALIGN_64 = 1, - ALIGN_128 = 2, - ALIGN_256 = 3 -}; - - -class NEONXEmitter; - -class FakeXEmitter -{ - friend struct OpArg; // for Write8 etc -private: - u8 *code, *startcode; - u8 *lastCacheFlushEnd; - u32 condition; - -protected: - inline void Write32(u32 value) {*(u32*)code = value; code+=4;} - -public: - FakeXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) { - condition = CC_AL << 28; - } - FakeXEmitter(u8 *code_ptr) { - code = code_ptr; - lastCacheFlushEnd = code_ptr; - startcode = code_ptr; - condition = CC_AL << 28; - } - virtual ~FakeXEmitter() {} - - void SetCodePointer(u8 *ptr, u8 *writePtr) {} - const u8 *GetCodePointer() const { return nullptr; } - - void SetCodePtr(u8 *ptr) {} - void ReserveCodeSpace(u32 bytes) {} - const u8 *AlignCode16() { return nullptr; } - const u8 *AlignCodePage() { return nullptr; } - const u8 *GetCodePtr() const { return nullptr; } - void FlushIcache() {} - void FlushIcacheSection(u8 *start, u8 *end) {} - u8 *GetWritableCodePtr() { return nullptr; } - - CCFlags GetCC() { return CCFlags(condition >> 28); } - void SetCC(CCFlags cond = CC_AL) {} - - // Special purpose instructions - - // Do nothing - void NOP(int count = 1) {} //nop padding - TODO: fast nop slides, for amd and intel (check their manuals) - -#ifdef CALL -#undef CALL -#endif - - void QuickCallFunction(FakeReg scratchreg, const void *func); - template void QuickCallFunction(FakeReg scratchreg, T func) { - QuickCallFunction(scratchreg, (const void *)func); - } -}; // class FakeXEmitter - - -// Everything that needs to generate machine code should inherit from this. -// You get memory management for free, plus, you can use all the MOV etc functions without -// having to prefix them with gen-> or something similar. -class FakeXCodeBlock : public CodeBlock { -public: - void PoisonMemory(int offset) override { - } -}; - -} // namespace - -#endif // _DOLPHIN_FAKE_CODEGEN_ diff --git a/Core/CMakeLists.txt b/Core/CMakeLists.txt index 34e9f16f7c..c84d0a269a 100644 --- a/Core/CMakeLists.txt +++ b/Core/CMakeLists.txt @@ -188,8 +188,6 @@ if(NOT LIBRETRO) HW/StereoResampler.h MIPS/MIPSAsm.cpp MIPS/MIPSAsm.h - MIPS/fake/FakeJit.cpp - MIPS/fake/FakeJit.h Util/GameManager.cpp Util/GameManager.h Util/VideoPlayer.cpp @@ -632,7 +630,6 @@ add_library(Core STATIC MIPS/MIPSTracer.h MIPS/JitCommon/JitCommon.cpp MIPS/JitCommon/JitCommon.h - MIPS/JitCommon/JitBlockCache.cpp MIPS/JitCommon/JitBlockCache.h MIPS/JitCommon/JitState.cpp MIPS/JitCommon/JitState.h diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b6a47a8d6f..ca1683c618 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -508,7 +508,6 @@ - @@ -694,7 +693,6 @@ - @@ -871,7 +869,6 @@ - diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index f293f6ac42..69b9d96243 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -85,9 +85,6 @@ {0201ff27-4fa8-4cf3-bbb9-accfdc0c7046} - - {678fa299-0ff7-4983-982d-2da47b52e238} - {067e3128-3aaf-4ed1-b19e-bab11606abe7} @@ -481,9 +478,6 @@ HLE\Libraries - - MIPS\JitCommon - Core @@ -925,9 +919,6 @@ Debugger\WebSocket - - MIPS\fake - Debugger @@ -2079,9 +2070,6 @@ Debugger\WebSocket - - MIPS\fake - Debugger diff --git a/Core/MIPS/ARM64/Arm64IRJit.cpp b/Core/MIPS/ARM64/Arm64IRJit.cpp index ca4136c020..3b99093dd0 100644 --- a/Core/MIPS/ARM64/Arm64IRJit.cpp +++ b/Core/MIPS/ARM64/Arm64IRJit.cpp @@ -20,6 +20,7 @@ #if PPSSPP_ARCH(ARM64) || (PPSSPP_PLATFORM(WINDOWS) && !defined(__LIBRETRO__)) #include +#include #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/ARM64/Arm64IRJit.h" diff --git a/Core/MIPS/IR/IRFrontend.cpp b/Core/MIPS/IR/IRFrontend.cpp index c79670cfeb..043716999a 100644 --- a/Core/MIPS/IR/IRFrontend.cpp +++ b/Core/MIPS/IR/IRFrontend.cpp @@ -278,10 +278,7 @@ void IRFrontend::DoJit(u32 em_address, std::vector &instructions, u32 &m js.cancel = false; js.blockStart = em_address; js.compilerPC = em_address; - js.initialBlockSize = 0; - js.nextExit = 0; js.downcountAmount = 0; - js.curBlock = nullptr; js.compiling = true; js.hadBreakpoints = false; js.blockWrotePrefixes = false; diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 2c1f2aaff4..f6f1cca8fb 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -245,14 +245,6 @@ bool IRJit::DescribeCodePtr(const u8 *ptr, std::string &name) { return false; } -void IRJit::LinkBlock(u8 *exitPoint, const u8 *checkedEntry) { - Crash(); -} - -void IRJit::UnlinkBlock(u8 *checkedEntry, u32 originalAddress) { - Crash(); -} - void IRBlockCache::Clear() { for (int i = 0; i < (int)blocks_.size(); ++i) { int cookie = compileToNative_ ? blocks_[i].GetNativeOffset() : blocks_[i].GetIRArenaOffset(); diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index 6ccdba5bd5..b40a8ed450 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -214,8 +214,6 @@ public: void Compile(u32 em_address) override; // Compiles a block at current MIPS PC bool DescribeCodePtr(const u8 *ptr, std::string &name) override; - // Not using a regular block cache. - JitBlockCache *GetBlockCache() override { return nullptr; } JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks_; } void ClearCache() override; @@ -229,9 +227,6 @@ public: const u8 *GetDispatcher() const override { return nullptr; } const u8 *GetCrashHandler() const override { return nullptr; } - void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override; - void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override; - // This gets overridden by the native-backed IR jits. const u8 *GetCodeBase() const override { return nullptr; } diff --git a/Core/MIPS/IR/IRNativeCommon.h b/Core/MIPS/IR/IRNativeCommon.h index 89d83ddb50..686d24406b 100644 --- a/Core/MIPS/IR/IRNativeCommon.h +++ b/Core/MIPS/IR/IRNativeCommon.h @@ -19,6 +19,7 @@ #include +#include "Common/CodeBlock.h" #include "Core/MIPS/IR/IRJit.h" #include "Core/MIPS/JitCommon/JitBlockCache.h" diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp deleted file mode 100644 index 133a436ffb..0000000000 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ /dev/null @@ -1,540 +0,0 @@ -// Copyright (c) 2012- PPSSPP Project / 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 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/. - -#include "ppsspp_config.h" -#include -#include - -#include "ext/xxhash.h" -#include "Common/CommonTypes.h" -#include "Common/Profiler/Profiler.h" - -#ifdef _WIN32 -#include "Common/CommonWindows.h" -#endif - -#include "Core/Core.h" -#include "Core/MemMap.h" -#include "Core/HLE/ReplaceTables.h" -#include "Core/CoreTiming.h" -#include "Core/Reporting.h" - -#include "Core/MIPS/MIPS.h" -#include "Core/MIPS/MIPSTables.h" -#include "Core/MIPS/MIPSAnalyst.h" - -#include "Core/MIPS/JitCommon/JitBlockCache.h" -#include "Core/MIPS/JitCommon/JitCommon.h" - -constexpr u32 INVALID_EXIT = 0xFFFFFFFF; -constexpr u32 SENTINEL_VAL = 0xc0ffeefe; - -static uint64_t HashJitBlock(const JitBlock &b) { - PROFILE_THIS_SCOPE("jithash"); - if (JIT_USE_COMPILEDHASH) { - if (Memory::IsValidRange(b.originalAddress, b.originalSize * 4)) { - return XXH3_64bits(Memory::GetPointerUnchecked(b.originalAddress), b.originalSize * 4); - } else { - // Hm, this would be bad. - return 0; - } - } - return 0; -} - -bool JitBlock::ContainsAddress(u32 address) const { - // WARNING - THIS DOES NOT WORK WITH JIT INLINING ENABLED. - // However, that doesn't exist yet so meh. - return address >= originalAddress && address < originalAddress + 4 * originalSize; -} - -void JitBlock::DoIntegrityCheck(u32 emAddress, int num, const char *reason) const { - _assert_msg_(emAddress == originalAddress, "%s: Bad orig %08x (expected: %08x, snt: %08x) in block %d (b.num: %d) sz: %d", reason, originalAddress, emAddress, sentinel, num, blockNum, codeSize); - // Also alert just if the sentinel got corrupted. - _assert_msg_(sentinel == SENTINEL_VAL, "%s: Block %d(%d) sentinel got corrupted: %08x != %08x (origAddr: %08x)", reason, num, blockNum, sentinel, SENTINEL_VAL, originalAddress); -} - -JitBlockCache::JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock) : codeBlock_(codeBlock) {} - -JitBlockCache::~JitBlockCache() { - Shutdown(); -} - -bool JitBlockCache::IsFull() const { - // Subtract some amount to safely leave space for some proxy blocks (now obsolete, but let's still have some extra margin). - return num_blocks_ >= MAX_NUM_BLOCKS - 64; -} - -void JitBlockCache::Init() { - blocks_ = new JitBlock[MAX_NUM_BLOCKS]; - Clear(); -} - -void JitBlockCache::Shutdown() { - Clear(); // Make sure proxy block links are deleted - delete [] blocks_; - blocks_ = 0; - num_blocks_ = 0; -} - -// 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() { - // Note: We intentionally clear the block_map_ first to avoid O(N^2) behavior in RemoveBlockMap - block_map_.clear(); - for (int i = 0; i < num_blocks_; i++) { - DestroyBlock(i, DestroyType::CLEAR); - } - links_to_.clear(); - num_blocks_ = 0; - - blockMemRanges_[JITBLOCK_RANGE_SCRATCH] = std::make_pair(0xFFFFFFFF, 0x00000000); - blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM] = std::make_pair(0xFFFFFFFF, 0x00000000); - blockMemRanges_[JITBLOCK_RANGE_RAMTOP] = std::make_pair(0xFFFFFFFF, 0x00000000); -} - -void JitBlockCache::Reset() { - Shutdown(); - Init(); -} - -int JitBlockCache::AllocateBlock(u32 startAddress) { - _assert_(num_blocks_ < MAX_NUM_BLOCKS); - - const int numBlocks = num_blocks_; - - JitBlock &b = blocks_[numBlocks]; - - b.invalid = false; - b.originalAddress = startAddress; - for (int i = 0; i < MAX_JIT_BLOCK_EXITS; ++i) { - b.exitAddress[i] = INVALID_EXIT; - b.exitPtrs[i] = 0; - b.linkStatus[i] = false; - } - b.blockNum = numBlocks; - b.sentinel = SENTINEL_VAL; - - num_blocks_ = numBlocks + 1; //commit the current block - return numBlocks; -} - -void JitBlockCache::AddBlockMap(int block_num) { - const JitBlock &b = blocks_[block_num]; - // Convert the logical address to a physical address for the block map - u32 pAddr = b.originalAddress & 0x1FFFFFFF; - block_map_[std::make_pair(pAddr + 4 * b.originalSize, pAddr)] = block_num; -} - -void JitBlockCache::RemoveBlockMap(int block_num) { - const JitBlock &b = blocks_[block_num]; - if (b.invalid) { - return; - } - - const u32 pAddr = b.originalAddress & 0x1FFFFFFF; - auto it = block_map_.find(std::make_pair(pAddr + 4 * b.originalSize, pAddr)); - if (it != block_map_.end() && it->second == (u32)block_num) { - block_map_.erase(it); - } else { - // It wasn't in there, or it has the wrong key. Let's search... - // TODO: This is O(n), so O(n^2) when called for every block. - for (auto it = block_map_.begin(); it != block_map_.end(); ++it) { - if (it->second == (u32)block_num) { - _dbg_assert_(false); - block_map_.erase(it); - break; - } - } - } -} - -static void ExpandRange(std::pair &range, u32 newStart, u32 newEnd) { - range.first = std::min(range.first, newStart); - range.second = std::max(range.second, newEnd); -} - -void JitBlockCache::FinalizeBlock(int block_num, bool block_link) { - JitBlock &b = blocks_[block_num]; - - b.originalFirstOpcode = Memory::ReadUnchecked_Instruction(b.originalAddress); - Memory::WriteBlockShadow(b.originalAddress, GetShadowValueForBlock(block_num)); - b.compiledHash = HashJitBlock(b); - - AddBlockMap(block_num); - - if (block_link) { - for (int i = 0; i < MAX_JIT_BLOCK_EXITS; i++) { - if (b.exitAddress[i] != INVALID_EXIT) { - links_to_.emplace(b.exitAddress[i], block_num); - } - } - - LinkBlock(block_num); - LinkBlockExits(block_num); - } - - const u32 blockEnd = b.originalAddress + b.originalSize * 4 - 4; - if (Memory::IsScratchpadAddress(b.originalAddress)) { - ExpandRange(blockMemRanges_[JITBLOCK_RANGE_SCRATCH], b.originalAddress, blockEnd); - } - const u32 halfUserMemory = (PSP_GetUserMemoryEnd() - PSP_GetUserMemoryBase()) / 2; - if (b.originalAddress < PSP_GetUserMemoryBase() + halfUserMemory) { - ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMBOTTOM], b.originalAddress, blockEnd); - } - // >=, not >: the bottom check above is strict, so a block sitting exactly on the midpoint - // would otherwise land in neither range. - if (blockEnd >= PSP_GetUserMemoryBase() + halfUserMemory) { - ExpandRange(blockMemRanges_[JITBLOCK_RANGE_RAMTOP], b.originalAddress, blockEnd); - } -} - -bool JitBlockCache::RangeMayHaveBlocks(u32 start, u32 end) const { - for (int i = 0; i < JITBLOCK_RANGE_COUNT; ++i) { - if (end >= blockMemRanges_[i].first && start <= blockMemRanges_[i].second) { - return true; - } - } - return false; -} - -static int binary_search(const JitBlock blocks_[], const u8 *baseoff, int imin, int imax) { - while (imin < imax) { - int imid = (imin + imax) >> 1; - if (blocks_[imid].normalEntry < baseoff) - imin = imid + 1; - else - imax = imid; - } - if ((imax == imin) && (blocks_[imin].normalEntry == baseoff)) - return imin; - else - return -1; -} - -int JitBlockCache::GetBlockNumberFromShadowValue(u32 value) const { - if (!num_blocks_ || value == 0) - return -1; - - const u8 *baseoff = codeBlock_->GetBasePtr() + value; - if (baseoff >= codeBlock_->GetCodePtr()) { - ERROR_LOG(Log::JIT, "JitBlockCache: Invalid block shadow value %08x", value); - return -1; - } - - int bl = binary_search(blocks_, baseoff, 0, num_blocks_ - 1); - if (bl >= 0 && blocks_[bl].invalid) { - return -1; - } else { - return bl; - } -} - -u32 JitBlockCache::GetShadowValueForBlock(int blockNum) const { - // Never 0, the dispatcher is at the start of the code space. - const u32 off = (u32)(blocks_[blockNum].normalEntry - codeBlock_->GetBasePtr()); - _dbg_assert_(off != 0); - return off; -} - -int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr) const { - if (!blocks_ || !Memory::IsValid4AlignedAddress(addr)) - return -1; - - // A replacement hook's value is the dispatcher's compile path, which isn't any block's entry. - int bl = GetBlockNumberFromShadowValue(Memory::ReadBlockShadow(addr)); - if (bl < 0) { - return -1; - } - - if (blocks_[bl].originalAddress != addr) - return -1; - - return bl; -} - -void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector *block_numbers) const { - for (int i = 0; i < num_blocks_; i++) - if (blocks_[i].ContainsAddress(em_address)) - block_numbers->push_back(i); -} - -int JitBlockCache::GetBlockNumberFromAddress(u32 em_address) const { - for (int i = 0; i < num_blocks_; i++) { - if (blocks_[i].ContainsAddress(em_address)) - return i; - } - return -1; -} - -u32 JitBlockCache::GetAddressFromBlockPtr(const u8 *ptr) const { - if (!codeBlock_->IsInSpace(ptr)) - return (u32)-1; - - for (int i = 0; i < num_blocks_; ++i) { - const auto &b = blocks_[i]; - if (!b.invalid && ptr >= b.checkedEntry && ptr < b.normalEntry + b.codeSize) { - return b.originalAddress; - } - } - - // It's in jit somewhere, but we must have deleted it. - return 0; -} - -MIPSOpcode JitBlockCache::GetOriginalFirstOp(int block_num) const { - if (block_num >= num_blocks_ || block_num < 0) { - return MIPSOpcode(block_num); - } - return blocks_[block_num].originalFirstOpcode; -} - -void JitBlockCache::LinkBlockExits(int i) { - JitBlock &b = blocks_[i]; - if (b.invalid) { - // This block is dead. Don't relink it. - return; - } - - for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) { - if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) { - int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]); - if (destinationBlock == -1) { - continue; - } - - JitBlock &eb = blocks_[destinationBlock]; - // Make sure the destination is not invalid. - if (!eb.invalid) { - MIPSComp::jit->LinkBlock(b.exitPtrs[e], eb.checkedEntry); - b.linkStatus[e] = true; - } - } - } -} - -void JitBlockCache::LinkBlock(int i) { - LinkBlockExits(i); - JitBlock &b = blocks_[i]; - // equal_range(b) returns pair representing the range - // of element with key b - auto ppp = links_to_.equal_range(b.originalAddress); - if (ppp.first == ppp.second) - return; - for (auto iter = ppp.first; iter != ppp.second; ++iter) { - // INFO_LOG(Log::JIT, "Linking block %i to block %i", iter->second, i); - LinkBlockExits(iter->second); - } -} - -void JitBlockCache::UnlinkBlock(int i) { - JitBlock &b = blocks_[i]; - auto ppp = links_to_.equal_range(b.originalAddress); - if (ppp.first == ppp.second) - return; - for (auto iter = ppp.first; iter != ppp.second; ++iter) { - if ((size_t)iter->second >= num_blocks_) { - // Something probably went very wrong. Try to stumble along nevertheless. - ERROR_LOG(Log::JIT, "UnlinkBlock: Invalid block number %d", iter->second); - continue; - } - JitBlock &sourceBlock = blocks_[iter->second]; - for (int e = 0; e < MAX_JIT_BLOCK_EXITS; e++) { - if (sourceBlock.exitAddress[e] == b.originalAddress) - sourceBlock.linkStatus[e] = false; - } - } -} - -void JitBlockCache::DestroyBlock(int block_num, DestroyType type) { - if (block_num < 0 || block_num >= num_blocks_) { - ERROR_LOG_REPORT(Log::JIT, "DestroyBlock: Invalid block number %d", block_num); - return; - } - JitBlock *b = &blocks_[block_num]; - // No point it being in there anymore. - RemoveBlockMap(block_num); - - // TODO: Handle the case when there's a proxy block and a regular JIT block at the same location. - // In this case we probably "leak" the proxy block currently (no memory leak but it'll stay enabled). - - if (b->invalid) { - if (type == DestroyType::INVALIDATE) - ERROR_LOG(Log::JIT, "Invalidating invalid block %d", block_num); - return; - } - - b->invalid = true; - - // Only clearing the shadow entry needs a valid address. The unlinking below has to - // happen either way - otherwise other blocks go on jumping straight into this one, which - // we've just marked invalid. - if (Memory::IsValid4AlignedAddress(b->originalAddress)) { - // A mirror may have replaced the entry with its own block, leave that alone. - Memory::ClearBlockShadow(b->originalAddress, GetShadowValueForBlock(block_num)); - } else { - _dbg_assert_msg_(false, "Destroying block with invalid original address: %08x (block num: %d)", b->originalAddress, block_num); - } - - // It's not safe to set normalEntry to 0 here, since we use a binary search - // that looks at that later to find blocks. Marking it invalid is enough. - - UnlinkBlock(block_num); - - if (b->checkedEntry) { - // We can skip this if we're clearing anyway, which cuts down on protect back and forth on WX exclusive. - if (type != DestroyType::CLEAR) { - u8 *writableEntry = codeBlock_->GetWritablePtrFromCodePtr(b->checkedEntry); - MIPSComp::jit->UnlinkBlock(writableEntry, b->originalAddress); - } - } else { - ERROR_LOG(Log::JIT, "Unlinking block with no entry: %08x (%d)", b->originalAddress, block_num); - } -} - -void JitBlockCache::InvalidateICache(u32 address, const u32 length) { - // Convert the logical address to a physical address for the block map - const u32 pAddr = address & 0x1FFFFFFF; - const u32 pEnd = pAddr + length; - - if (pEnd < pAddr) { - ERROR_LOG(Log::JIT, "Bad InvalidateICache: %08x with len=%d", address, length); - return; - } - - if (pAddr == 0 && pEnd >= 0x1FFFFFFF) { - InvalidateChangedBlocks(); - return; - } - - // Blocks may start and end in overlapping ways, and destroying one invalidates iterators. - // So after destroying one, we start over. - do { - restart: - auto next = block_map_.lower_bound(std::make_pair(pAddr, 0)); - // block_map_ is keyed by byte addresses, so the margin has to cover the longest - // possible block in bytes, not instructions. - auto last = block_map_.upper_bound(std::make_pair(pEnd + 4 * MAX_BLOCK_INSTRUCTIONS, 0)); - // Note that if next is end(), last will be end() too (equal.) - for (; next != last; ++next) { - const u32 blockStart = next->first.second; - const u32 blockEnd = next->first.first; - if (blockStart < pEnd && blockEnd > pAddr) { - DestroyBlock(next->second, DestroyType::INVALIDATE); - // Our iterator is now invalid. Break and search again. - // Most of the time there shouldn't be a bunch of matching blocks. - goto restart; - } - } - // We got here - it wasn't in the map at all (or anymore.) - } while (false); -} - -void JitBlockCache::InvalidateChangedBlocks() { - // The primary goal of this is to make sure block linking is cleared up. - for (int block_num = 0; block_num < num_blocks_; ++block_num) { - JitBlock &b = blocks_[block_num]; - if (b.invalid) - continue; - - bool changed = false; - if (JIT_USE_COMPILEDHASH) { - changed = b.compiledHash != HashJitBlock(b); - } else { - changed = Memory::ReadUnchecked_U32(b.originalAddress) != b.originalFirstOpcode.encoding; - } - - if (changed) { - DEBUG_LOG(Log::JIT, "Invalidating changed block at %08x", b.originalAddress); - DestroyBlock(block_num, DestroyType::INVALIDATE); - } - } -} - -int JitBlockCache::GetBlockExitSize() { -#if PPSSPP_ARCH(ARM) - // Will depend on the sequence found to encode the destination address. - return 0; -#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) - return 15; -#elif PPSSPP_ARCH(ARM64) - // Will depend on the sequence found to encode the destination address. - return 0; -#elif PPSSPP_ARCH(RISCV64) - // Will depend on the sequence found to encode the destination address. - return 0; -#elif PPSSPP_ARCH(LOONGARCH64) - // Will depend on the sequence found to encode the destination address. - return 0; -#else -#warning GetBlockExitSize unimplemented - return 0; -#endif -} - -void JitBlockCache::ComputeStats(BlockCacheStats &bcStats) const { - double totalBloat = 0.0; - double maxBloat = 0.0; - double minBloat = 1000000000.0; - for (int i = 0; i < num_blocks_; i++) { - const JitBlock *b = GetBlock(i); - double codeSize = (double)b->codeSize; - if (codeSize == 0) - continue; - double origSize = (double)(4 * b->originalSize); - double bloat = codeSize / origSize; - if (bloat < minBloat) { - minBloat = bloat; - bcStats.minBloatBlock = b->originalAddress; - } - if (bloat > maxBloat) { - maxBloat = bloat; - bcStats.maxBloatBlock = b->originalAddress; - } - totalBloat += bloat; - } - bcStats.numBlocks = num_blocks_; - bcStats.minBloat = (float)minBloat; - bcStats.maxBloat = (float)maxBloat; - bcStats.avgBloat = (float)(totalBloat / (double)num_blocks_); -} - -JitBlockDebugInfo JitBlockCache::GetBlockDebugInfo(int blockNum) const { - JitBlockDebugInfo debugInfo{}; - const JitBlock *block = GetBlock(blockNum); - debugInfo.originalAddress = block->originalAddress; - debugInfo.origDisasm.reserve(((block->originalAddress + block->originalSize * 4) - block->originalAddress) / 4); - for (u32 addr = block->originalAddress; addr < block->originalAddress + block->originalSize * 4; addr += 4) { - char temp[256]; - MIPSDisAsm(Replacement_GetDisplayOp(addr), addr, temp, sizeof(temp), true); - std::string mipsDis = temp; - debugInfo.origDisasm.push_back(mipsDis); - } - -#if PPSSPP_ARCH(ARM) - debugInfo.targetDisasm = DisassembleArm2(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(ARM64) - debugInfo.targetDisasm = DisassembleArm64(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) - debugInfo.targetDisasm = DisassembleX86(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(RISCV64) - debugInfo.targetDisasm = DisassembleRV64(block->normalEntry, block->codeSize); -#elif PPSSPP_ARCH(LOONGARCH64) - debugInfo.targetDisasm = DisassembleLA64(block->normalEntry, block->codeSize); -#endif - return debugInfo; -} diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index a39f37187b..f9ecb7b672 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -18,20 +18,10 @@ #pragma once #include -#include -#include -#include #include +#include -#include "ppsspp_config.h" #include "Common/CommonTypes.h" -#include "Common/CodeBlock.h" -#include "Core/MIPS/MIPS.h" - -// Can probably reduce to 2 now that block continuation is gone. -const int MAX_JIT_BLOCK_EXITS = 4; - -constexpr bool JIT_USE_COMPILEDHASH = true; struct BlockCacheStats { int numBlocks; @@ -42,41 +32,6 @@ struct BlockCacheStats { u32 maxBloatBlock; }; -enum class DestroyType { - DESTROY, - INVALIDATE, - // Skips jit unlink, since it'll be poisoned anyway. - CLEAR, -}; - -// We should be careful not to access these block structures during runtime as they are large. -// Fine to mess with them at block compile time though. -struct JitBlock { - bool ContainsAddress(u32 address) const; - void DoIntegrityCheck(u32 emAddress, int blockNum, const char *reason) const; - - const u8 *checkedEntry; // const, we have to translate to writable. - const u8 *normalEntry; - - u8 *exitPtrs[MAX_JIT_BLOCK_EXITS]; // to be able to rewrite the exit jump - u32 exitAddress[MAX_JIT_BLOCK_EXITS]; // 0xFFFFFFFF == unknown - u32 sentinel; - - u32 originalAddress; - MIPSOpcode originalFirstOpcode; //to be able to restore - - uint64_t compiledHash; - - u32 codeSize; // in bytes, so it doesn't fit in a u16 for a long block - u16 originalSize; // in instructions, bounded by MAX_BLOCK_INSTRUCTIONS - u16 blockNum; - - bool invalid; - bool linkStatus[MAX_JIT_BLOCK_EXITS]; -}; - -typedef void (*CompiledCode)(); - struct JitBlockDebugInfo { uint32_t originalAddress; std::vector origDisasm; @@ -109,100 +64,3 @@ public: virtual ~JitBlockCacheDebugInterface() {} }; - -class JitBlockCache : public JitBlockCacheDebugInterface { -public: - JitBlockCache(MIPSState *mipsState, CodeBlockCommon *codeBlock); - ~JitBlockCache(); - - int AllocateBlock(u32 emAddress); - void FinalizeBlock(int block_num, bool block_link); - - void Clear(); - void Init(); - void Shutdown(); - void Reset(); - - bool IsFull() const; - void ComputeStats(BlockCacheStats &bcStats) const override; - - // Code Cache - JitBlock *GetBlock(int no) { return &blocks_[no]; } - const JitBlock *GetBlock(int no) const { return &blocks_[no]; } - - // Fast way to get a block. Only works on the first source-cpu instruction of a block. - int GetBlockNumberFromStartAddress(u32 em_address) const override; - - // slower, but can get numbers from within blocks, not just the first instruction. - // WARNING! WILL NOT WORK WITH JIT INLINING ENABLED (not yet a feature but will be soon) - // Returns a list of block numbers - only one block can start at a particular address, but they CAN overlap. - // This one is slow so should only be used for one-shots from the debugger UI, not for anything during runtime. - void GetBlockNumbersFromAddress(u32 em_address, std::vector *block_numbers) const; - // Similar to above, but only the first matching address. - int GetBlockNumberFromAddress(u32 em_address) const; - int GetBlockNumberFromShadowValue(u32 value) const; - - u32 GetAddressFromBlockPtr(const u8 *ptr) const; - - MIPSOpcode GetOriginalFirstOp(int block_num) const; - - bool RangeMayHaveBlocks(u32 start, u32 end) const; - - // DOES NOT WORK CORRECTLY WITH JIT INLINING - void InvalidateICache(u32 address, const u32 length); - void InvalidateChangedBlocks(); - void DestroyBlock(int block_num, DestroyType type); - - int GetNumBlocks() const override { return num_blocks_; } - bool IsValidBlock(int blockNum) const override { return blockNum >= 0 && blockNum < num_blocks_ && !blocks_[blockNum].invalid; } - JitBlockMeta GetBlockMeta(int blockNum) const override { - JitBlockMeta meta{}; - if (IsValidBlock(blockNum)) { - meta.valid = true; - meta.addr = blocks_[blockNum].originalAddress; - meta.sizeInBytes = blocks_[blockNum].originalSize; - } - return meta; - } - JitBlockProfileStats GetBlockProfileStats(int blockNum) const override { - return JitBlockProfileStats{}; - } - - static int GetBlockExitSize(); - - JitBlockDebugInfo GetBlockDebugInfo(int blockNum) const override; - - enum { - MAX_BLOCK_INSTRUCTIONS = 0x4000, - }; - -private: - void LinkBlockExits(int i); - void LinkBlock(int i); - void UnlinkBlock(int i); - - void AddBlockMap(int block_num); - void RemoveBlockMap(int block_num); - - // What the block shadow holds for a block: its normal entry's offset into the code space. - u32 GetShadowValueForBlock(int block_num) const; - - int num_blocks_ = 0; - CodeBlockCommon *codeBlock_; - JitBlock *blocks_ = nullptr; - std::unordered_multimap links_to_; - std::map, u32> block_map_; // (end_addr, start_addr) -> number - - enum { - JITBLOCK_RANGE_SCRATCH = 0, - JITBLOCK_RANGE_RAMBOTTOM = 1, - JITBLOCK_RANGE_RAMTOP = 2, - JITBLOCK_RANGE_COUNT = 3, - }; - std::pair blockMemRanges_[JITBLOCK_RANGE_COUNT]; - - enum { - // Where does this number come from? - MAX_NUM_BLOCKS = 65536 * 4 - }; -}; diff --git a/Core/MIPS/JitCommon/JitCommon.cpp b/Core/MIPS/JitCommon/JitCommon.cpp index 1a065d9935..06c1494bf9 100644 --- a/Core/MIPS/JitCommon/JitCommon.cpp +++ b/Core/MIPS/JitCommon/JitCommon.cpp @@ -48,8 +48,6 @@ #include "../RiscV/RiscVJit.h" #elif PPSSPP_ARCH(LOONGARCH64) #include "../LoongArch64/LoongArch64Jit.h" -#else -#include "../fake/FakeJit.h" #endif namespace MIPSComp { @@ -115,7 +113,8 @@ namespace MIPSComp { #elif PPSSPP_ARCH(LOONGARCH64) return new MIPSComp::LoongArch64Jit(mipsState); #else - return new MIPSComp::FakeJit(mipsState); + // No native JIT for this architecture. + return new MIPSComp::IRJit(mipsState, false); #endif } diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index 4dc09a892c..ea17846606 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -31,8 +31,6 @@ std::vector DisassembleX86(const u8 *data, int size); std::vector DisassembleRV64(const u8 *data, int size); std::vector DisassembleLA64(const u8 *data, int size); -struct JitBlock; -class JitBlockCache; class JitBlockCacheDebugInterface; class PointerWrap; @@ -127,7 +125,6 @@ namespace MIPSComp { } virtual const u8 *GetDispatcher() const = 0; virtual const u8 *GetCrashHandler() const = 0; - virtual JitBlockCache *GetBlockCache() = 0; virtual JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() = 0; virtual void InvalidateCacheAt(u32 em_address, int length = 4) = 0; virtual void DoState(PointerWrap &p) = 0; @@ -138,11 +135,6 @@ namespace MIPSComp { // What the block shadow holds for a replacement hook without a compiled block. The // dispatcher has to send this to the compiler. virtual u32 GetBlockShadowHookValue() const { return Memory::BLOCK_SHADOW_HOOK_NO_JIT; } - - // Block linking. This may need to work differently for whole-function JITs and stuff - // like that. - virtual void LinkBlock(u8 *exitPoint, const u8 *entryPoint) = 0; - virtual void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) = 0; }; typedef void (MIPSFrontendInterface::*MIPSCompileFunc)(MIPSOpcode opcode); diff --git a/Core/MIPS/JitCommon/JitState.cpp b/Core/MIPS/JitCommon/JitState.cpp index 458fdb56f6..c77b6ab0ee 100644 --- a/Core/MIPS/JitCommon/JitState.cpp +++ b/Core/MIPS/JitCommon/JitState.cpp @@ -19,28 +19,11 @@ #include "Common/CPUDetect.h" #include "Core/Config.h" #include "Core/MIPS/JitCommon/JitState.h" -#include "Core/MIPS/JitCommon/JitBlockCache.h" #include "Core/MemMap.h" #include "Common/MemoryUtil.h" namespace MIPSComp { -void JitState::Begin(JitBlock *block) { - cancel = false; - blockStart = block->originalAddress; - compilerPC = block->originalAddress; - initialBlockSize = 0; - nextExit = 0; - downcountAmount = 0; - curBlock = block; - compiling = true; - inDelaySlot = false; - blockWrotePrefixes = false; - afterOp = JitState::AFTER_NONE; - kernelMode = Memory::IsKernelCodeAddress(block->originalAddress); - PrefixStart(); -} - JitOptions::JitOptions() { disableFlags = (JitDisable)g_Config.uJitDisableFlags; @@ -61,11 +44,7 @@ void JitState::Begin(JitBlock *block) { // We can get block linking to work with W^X by doing even more unprotect/re-protect, but let's try without first. // enableBlocklink = !PlatformIsWXExclusive(); // Revert to this line if block linking is slow in W^X mode -#if PPSSPP_ARCH(MIPS) - enableBlocklink = false; -#else enableBlocklink = !Disabled(JitDisable::BLOCKLINK); -#endif useStaticAlloc = false; enablePointerify = false; #if PPSSPP_ARCH(ARM64) || PPSSPP_ARCH(RISCV64) || PPSSPP_ARCH(LOONGARCH64) diff --git a/Core/MIPS/JitCommon/JitState.h b/Core/MIPS/JitCommon/JitState.h index a5f2725dd4..65c7b57c24 100644 --- a/Core/MIPS/JitCommon/JitState.h +++ b/Core/MIPS/JitCommon/JitState.h @@ -23,22 +23,8 @@ #include "Common/Log.h" #include "Core/MIPS/MIPS.h" -struct JitBlock; -class JitBlockCache; - namespace MIPSComp { - enum CompileDelaySlotFlags { - // Easy, nothing extra. - DELAYSLOT_NICE = 0, - // Flush registers after delay slot. - DELAYSLOT_FLUSH = 1, - // Preserve flags. - DELAYSLOT_SAFE = 2, - // Flush registers after and preserve flags. - DELAYSLOT_SAFE_FLUSH = DELAYSLOT_FLUSH | DELAYSLOT_SAFE, - }; - struct JitState { enum PrefixState { PREFIX_UNKNOWN = 0x00, @@ -47,29 +33,18 @@ namespace MIPSComp { PREFIX_KNOWN_DIRTY = 0x11, }; - enum AfterOp { - AFTER_NONE = 0x00, - AFTER_CORE_STATE = 0x01, - }; - u32 compilerPC; u32 blockStart; - u32 initialBlockSize; - int nextExit; bool cancel; bool inDelaySlot; bool kernelMode; // Changes what instructions are allowed. - // See JitState::AfterOp for values. - int afterOp; int downcountAmount; int numInstructions; bool compiling; // TODO: get rid of this in favor of using analysis results to determine end of block bool hadBreakpoints; - JitBlock *curBlock; u8 hasSetRounding = 0; u8 lastSetRounding = 0; - const u8 *currentRoundingFunc = nullptr; // VFPU prefix magic bool startDefaultPrefix = true; @@ -81,8 +56,6 @@ namespace MIPSComp { PrefixState prefixTFlag = PREFIX_UNKNOWN; PrefixState prefixDFlag = PREFIX_UNKNOWN; - void Begin(JitBlock *block); - void PrefixStart() { if (startDefaultPrefix) { EatPrefix(); diff --git a/Core/MIPS/LoongArch64/LoongArch64Jit.cpp b/Core/MIPS/LoongArch64/LoongArch64Jit.cpp index eb9e56570e..dd2db5cc27 100644 --- a/Core/MIPS/LoongArch64/LoongArch64Jit.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64Jit.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/LoongArch64/LoongArch64Jit.h" diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index 7d9d9fe7a1..4cc2bf72de 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/RiscV/RiscVJit.h" diff --git a/Core/MIPS/fake/FakeJit.cpp b/Core/MIPS/fake/FakeJit.cpp deleted file mode 100644 index 58d08260ba..0000000000 --- a/Core/MIPS/fake/FakeJit.cpp +++ /dev/null @@ -1,227 +0,0 @@ -// 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/. - -#include "Common/Serialize/Serializer.h" -#include "Common/Serialize/SerializeFuncs.h" -#include "Core/Reporting.h" -#include "Core/Core.h" -#include "Core/CoreTiming.h" -#include "Core/Debugger/Breakpoints.h" -#include "Core/Debugger/SymbolMap.h" -#include "Core/MemMap.h" -#include "Core/MIPS/MIPS.h" -#include "Core/MIPS/MIPSCodeUtils.h" -#include "Core/MIPS/Interpreter.h" -#include "Core/MIPS/MIPSTables.h" -#include "Core/HLE/ReplaceTables.h" - -#include "FakeEmitter.h" -#include "FakeJit.h" -#include "CPUDetect.h" - -void DisassembleFake(const u8 *data, int size) { -} - -namespace MIPSComp -{ - -FakeJit::FakeJit(MIPSState *mipsState) : blocks(mipsState, this), mips_(mipsState), js() -{ - logBlocks = 0; - dontLogBlocks = 0; - blocks.Init(); -} - -void FakeJit::DoState(PointerWrap &p) { - auto s = p.Section("Jit", 1, 2); - if (!s) - return; - - Do(p, js.startDefaultPrefix); - if (s >= 2) { - Do(p, js.hasSetRounding); - if (p.mode == PointerWrap::MODE_READ) { - js.lastSetRounding = 0; - } - } else { - js.hasSetRounding = 1; - } - - // The debugger sets this so that "go" on a breakpoint will actually... go. - // But if they load a state, we can end up hitting it by mistake, since it's based on PC and ticks. - g_breakpoints.SetSkipFirst(0); -} - -// This is here so the savestate matches between jit and non-jit. -void FakeJit::DoDummyState(PointerWrap &p) -{ - auto s = p.Section("FakeJit", 1, 2); - if (!s) - return; - - bool dummy = false; - Do(p, dummy); - if (s >= 2) { - dummy = true; - Do(p, dummy); - } -} - -void FakeJit::FlushAll() -{ - //gpr.FlushAll(); - //fpr.FlushAll(); - FlushPrefixV(); -} - -void FakeJit::FlushPrefixV() -{ -} - -void FakeJit::ClearCache() -{ - blocks.Clear(); - ClearCodeSpace(0); - //GenerateFixedCode(); -} - -void FakeJit::InvalidateCacheAt(u32 em_address, int length) { - if (blocks.RangeMayHaveBlocks(em_address, em_address + length)) { - blocks.InvalidateICache(em_address, length); - } -} - -void FakeJit::EatInstruction(MIPSOpcode op) { - MIPSInfo info = MIPSGetInfo(op); - if (info & DELAYSLOT) { - ERROR_LOG_REPORT_ONCE(ateDelaySlot, Log::JIT, "Ate a branch op."); - } - if (js.inDelaySlot) { - ERROR_LOG_REPORT_ONCE(ateInDelaySlot, Log::JIT, "Ate an instruction inside a delay slot."); - } - - js.numInstructions++; - js.compilerPC += 4; - js.downcountAmount += MIPSGetInstructionCycleEstimate(op); -} - -void FakeJit::CompileDelaySlot(int flags) -{ -} - - -void FakeJit::Compile(u32 em_address) { -} - -void FakeJit::RunLoopUntil(u64 globalticks) { - MIPSInterpret_RunUntil(currentMIPS, globalticks); -} - -void FakeJit::DoJit(u32 em_address, JitBlock *b) { - _assert_(false); -} - -bool FakeJit::DescribeCodePtr(const u8 *ptr, std::string &name) -{ - // TODO: Not used by anything yet. - return false; -} - -void FakeJit::Comp_ReplacementFunc(MIPSOpcode op) -{ -} - -void FakeJit::Comp_Generic(MIPSOpcode op) -{ - FlushAll(); - MIPSInterpretFunc func = MIPSGetInterpretFunc(op); - if (func) - { - SaveDowncount(); - RestoreDowncount(); - } - - const MIPSInfo info = MIPSGetInfo(op); - if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) - { - // If it does eat them, it'll happen in MIPSCompileOp(). - if ((info & OUT_EAT_PREFIX) == 0) - js.PrefixUnknown(); - } -} - -void FakeJit::MovFromPC(FakeReg r) { -} - -void FakeJit::MovToPC(FakeReg r) { -} - -void FakeJit::SaveDowncount() { -} - -void FakeJit::RestoreDowncount() { -} - -void FakeJit::WriteDownCount(int offset) { -} - -// Abuses R2 -void FakeJit::WriteDownCountR(FakeReg reg) { -} - -void FakeJit::RestoreRoundingMode(bool force) { -} - -void FakeJit::ApplyRoundingMode(bool force) { -} - -void FakeJit::UpdateRoundingMode() { -} - -void FakeJit::WriteExit(u32 destination, int exit_num) -{ -} - -void FakeJit::WriteExitDestInR(FakeReg Reg) -{ -} - -void FakeJit::WriteSyscallExit() -{ -} - -#define _RS ((op>>21) & 0x1F) -#define _RT ((op>>16) & 0x1F) -#define _RD ((op>>11) & 0x1F) -#define _FS ((op>>11) & 0x1F) -#define _FT ((op>>16) & 0x1F) -#define _FD ((op>>6) & 0x1F) -#define _POS ((op>>6) & 0x1F) -#define _SIZE ((op>>11) & 0x1F) - -//memory regions: -// -// 08-0A -// 48-4A -// 04-05 -// 44-45 -// mov eax, addrreg - // shr eax, 28 -// mov eax, [table+eax] -// mov dreg, [eax+offreg] - -} diff --git a/Core/MIPS/fake/FakeJit.h b/Core/MIPS/fake/FakeJit.h deleted file mode 100644 index 8f325cccb8..0000000000 --- a/Core/MIPS/fake/FakeJit.h +++ /dev/null @@ -1,177 +0,0 @@ -// 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 - -#include "Common/FakeEmitter.h" -#include "Core/MIPS/JitCommon/JitBlockCache.h" -#include "Core/MIPS/JitCommon/JitState.h" -#include "Core/MIPS/JitCommon/JitCommon.h" -#include "../MIPSVFPUUtils.h" - -#ifndef offsetof -#include "stddef.h" -#endif - -namespace MIPSComp { - -typedef int FakeReg; - -class FakeJit : public FakeGen::FakeXCodeBlock, public JitInterface, public MIPSFrontendInterface { -public: - FakeJit(MIPSState *mipsState); - - void DoState(PointerWrap &p) override; - static void DoDummyState(PointerWrap &p); - - const JitOptions &GetJitOptions() { return jo; } - - void Comp_Generic(MIPSOpcode op) override; - - void RunLoopUntil(u64 globalticks) override; - - void Compile(u32 em_address) override; // Compiles a block at current MIPS PC - void DoJit(u32 em_address, JitBlock *b); - - const u8 *GetCrashHandler() const override { return nullptr; } - bool CodeInRange(const u8 *ptr) const override { return IsInSpace(ptr); } - bool DescribeCodePtr(const u8 *ptr, std::string &name) override; - - void CompileDelaySlot(int flags); - void EatInstruction(MIPSOpcode op); - - void Comp_ReplacementFunc(MIPSOpcode op) override; - - // Ops - void Comp_ITypeMem(MIPSOpcode op) override {} - void Comp_StoreSync(MIPSOpcode op) override {} - void Comp_Cache(MIPSOpcode op) override {} - - void Comp_RelBranch(MIPSOpcode op) override {} - void Comp_RelBranchRI(MIPSOpcode op) override {} - void Comp_FPUBranch(MIPSOpcode op) override {} - void Comp_FPULS(MIPSOpcode op) override {} - void Comp_FPUComp(MIPSOpcode op) override {} - void Comp_Jump(MIPSOpcode op) override {} - void Comp_JumpReg(MIPSOpcode op) override {} - void Comp_Syscall(MIPSOpcode op) override {} - void Comp_Break(MIPSOpcode op) override {} - - void Comp_IType(MIPSOpcode op) override {} - void Comp_RType2(MIPSOpcode op) override {} - void Comp_RType3(MIPSOpcode op) override {} - void Comp_ShiftType(MIPSOpcode op) override {} - void Comp_Allegrex(MIPSOpcode op) override {} - void Comp_Allegrex2(MIPSOpcode op) override {} - void Comp_VBranch(MIPSOpcode op) override {} - void Comp_MulDivType(MIPSOpcode op) override {} - void Comp_Special3(MIPSOpcode op) override {} - - void Comp_FPU3op(MIPSOpcode op) override {} - void Comp_FPU2op(MIPSOpcode op) override {} - void Comp_mxc1(MIPSOpcode op) override {} - - void Comp_DoNothing(MIPSOpcode op) override {} - - void Comp_SV(MIPSOpcode op) override {} - void Comp_SVQ(MIPSOpcode op) override {} - void Comp_VPFX(MIPSOpcode op) override {} - void Comp_VVectorInit(MIPSOpcode op) override {} - void Comp_VMatrixInit(MIPSOpcode op) override {} - void Comp_VDot(MIPSOpcode op) override {} - void Comp_VecDo3(MIPSOpcode op) override {} - void Comp_VV2Op(MIPSOpcode op) override {} - void Comp_Mftv(MIPSOpcode op) override {} - void Comp_Vmfvc(MIPSOpcode op) override {} - void Comp_Vmtvc(MIPSOpcode op) override {} - void Comp_Vmmov(MIPSOpcode op) override {} - void Comp_VScl(MIPSOpcode op) override {} - void Comp_Vmmul(MIPSOpcode op) override {} - void Comp_Vmscl(MIPSOpcode op) override {} - void Comp_Vtfm(MIPSOpcode op) override {} - void Comp_VHdp(MIPSOpcode op) override {} - void Comp_VCrs(MIPSOpcode op) override {} - void Comp_VDet(MIPSOpcode op) override {} - void Comp_Vi2x(MIPSOpcode op) override {} - void Comp_Vx2i(MIPSOpcode op) override {} - void Comp_Vf2i(MIPSOpcode op) override {} - void Comp_Vi2f(MIPSOpcode op) override {} - void Comp_Vh2f(MIPSOpcode op) override {} - void Comp_Vcst(MIPSOpcode op) override {} - void Comp_Vhoriz(MIPSOpcode op) override {} - void Comp_VRot(MIPSOpcode op) override {} - void Comp_VIdt(MIPSOpcode op) override {} - void Comp_Vcmp(MIPSOpcode op) override {} - void Comp_Vcmov(MIPSOpcode op) override {} - void Comp_Viim(MIPSOpcode op) override {} - void Comp_Vfim(MIPSOpcode op) override {} - void Comp_VCrossQuat(MIPSOpcode op) override {} - void Comp_Vsgn(MIPSOpcode op) override {} - void Comp_Vocp(MIPSOpcode op) override {} - void Comp_ColorConv(MIPSOpcode op) override {} - void Comp_Vbfy(MIPSOpcode op) override {} - - int Replace_fabsf() override { return 0; } - - JitBlockCache *GetBlockCache() override { return &blocks; } - JitBlockCacheDebugInterface *GetBlockCacheDebugInterface() override { return &blocks; } - - void ClearCache() override; - void InvalidateCacheAt(u32 em_address, int length = 4) override; - void UpdateFCR31() override {} - - const u8 *GetDispatcher() const override { - return nullptr; - } - - void LinkBlock(u8 *exitPoint, const u8 *checkedEntry) override {} - void UnlinkBlock(u8 *checkedEntry, u32 originalAddress) override {} - - void EatPrefix() override { js.EatPrefix(); } - -private: - void GenerateFixedCode(); - void FlushAll(); - void FlushPrefixV(); - - void WriteDownCount(int offset = 0); - void WriteDownCountR(FakeReg reg); - void RestoreRoundingMode(bool force = false); - void ApplyRoundingMode(bool force = false); - void UpdateRoundingMode(); - void MovFromPC(FakeReg r); - void MovToPC(FakeReg r); - - void SaveDowncount(); - void RestoreDowncount(); - - void WriteExit(u32 destination, int exit_num); - void WriteExitDestInR(FakeReg Reg); - void WriteSyscallExit(); - - JitBlockCache blocks; - JitOptions jo; - JitState js; - - MIPSState *mips_; - - int dontLogBlocks; - int logBlocks; -}; - -} // namespace MIPSComp - diff --git a/Core/MIPS/x86/X64IRJit.cpp b/Core/MIPS/x86/X64IRJit.cpp index c391e97985..d9c1195c8d 100644 --- a/Core/MIPS/x86/X64IRJit.cpp +++ b/Core/MIPS/x86/X64IRJit.cpp @@ -19,6 +19,7 @@ #if PPSSPP_ARCH(X86) || PPSSPP_ARCH(AMD64) #include +#include #include "Common/StringUtils.h" #include "Core/MemMap.h" #include "Core/MIPS/MIPSTables.h" diff --git a/GPU/Common/VertexDecoderCommon.cpp b/GPU/Common/VertexDecoderCommon.cpp index 4dfee2a768..eed7049be8 100644 --- a/GPU/Common/VertexDecoderCommon.cpp +++ b/GPU/Common/VertexDecoderCommon.cpp @@ -1629,6 +1629,7 @@ std::string VertexDecoder::GetString(DebugShaderStringType stringType) const { } } +#ifdef VERTEXDECODER_JIT_BACKEND VertexDecoderJitCache::VertexDecoderJitCache() #if PPSSPP_ARCH(ARM64) : fp(this) @@ -1653,6 +1654,7 @@ VertexDecoderJitCache::VertexDecoderJitCache() void VertexDecoderJitCache::Clear() { ClearCodeSpace(0); } +#endif struct StepFunctionNameEntry { StepFunction func; diff --git a/GPU/Common/VertexDecoderCommon.h b/GPU/Common/VertexDecoderCommon.h index d11e74f023..3d63dddad1 100644 --- a/GPU/Common/VertexDecoderCommon.h +++ b/GPU/Common/VertexDecoderCommon.h @@ -39,8 +39,6 @@ #include "Common/RiscVEmitter.h" #elif PPSSPP_ARCH(LOONGARCH64) #include "Common/LoongArch64Emitter.h" -#else -#include "Common/FakeEmitter.h" #endif // Keep this in 4 bits. @@ -432,13 +430,13 @@ private: #endif }; #else -class VertexDecoderJitCache : public FakeGen::FakeXCodeBlock { +// No vertex decoder JIT for this architecture. +class VertexDecoderJitCache { public: - VertexDecoderJitCache(); - JittedVertexDecoder Compile(const VertexDecoder &dec, int32_t *jittedSize) { return nullptr; } - void Clear(); + void Clear() {} + bool IsInSpace(const u8 *ptr) const { return false; } }; #endif diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index 8f32259f25..a824729edd 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -848,8 +848,6 @@ static void DrawInternals(ImConfig &cfg) { ImGui::Text("Base pointer: %p", Memory::base); ImGui::Text("Main memory size: %08x", Memory::g_MemorySize); if (MIPSComp::jit) { - const JitBlockCache *bc = MIPSComp::jit->GetBlockCache(); - ImGui::Text("JIT block cache: %p", bc->GetBlock(0)); ImGui::Text("JIT code cache: %p", MIPSComp::jit->GetCodeBase()); ImGui::Text("MIPS state: %p", currentMIPS); } diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index 59badf30a4..b20455fa64 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -587,7 +587,6 @@ - diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index 9d0f9ce361..a0d6e0a145 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -214,7 +214,6 @@ - diff --git a/android/jni/Android.mk b/android/jni/Android.mk index dd00926e83..9a20982d63 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -844,7 +844,6 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/FileSystems/VirtualDiscFileSystem.cpp \ $(SRC)/Core/FileSystems/tlzrc.cpp \ $(SRC)/Core/MIPS/JitCommon/JitCommon.cpp \ - $(SRC)/Core/MIPS/JitCommon/JitBlockCache.cpp \ $(SRC)/Core/MIPS/JitCommon/JitState.cpp \ $(SRC)/Core/Util/AtracTrack.cpp \ $(SRC)/Core/Util/AudioFormat.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index abb9c6accb..3d7282e419 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -858,7 +858,6 @@ SOURCES_CXX += \ $(COREDIR)/Loaders.cpp \ $(COREDIR)/MIPS/JitCommon/JitCommon.cpp \ $(COREDIR)/MIPS/JitCommon/JitState.cpp \ - $(COREDIR)/MIPS/JitCommon/JitBlockCache.cpp \ $(COREDIR)/MIPS/IR/IRAnalysis.cpp \ $(COREDIR)/MIPS/IR/IRCompALU.cpp \ $(COREDIR)/MIPS/IR/IRCompBranch.cpp \