diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ca3c64c92..1a91796708 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1539,6 +1539,8 @@ set(CoreExtra) set(CoreExtraLibs) set(CoreExtra ${CoreExtra} + Core/MIPS/IR/IRAnalysis.cpp + Core/MIPS/IR/IRAnalysis.h Core/MIPS/IR/IRCompALU.cpp Core/MIPS/IR/IRCompBranch.cpp Core/MIPS/IR/IRCompFPU.cpp diff --git a/Core/Core.vcxproj b/Core/Core.vcxproj index b2ae24174f..9794f02f81 100644 --- a/Core/Core.vcxproj +++ b/Core/Core.vcxproj @@ -580,6 +580,7 @@ + @@ -1164,6 +1165,7 @@ + diff --git a/Core/Core.vcxproj.filters b/Core/Core.vcxproj.filters index a4f56f5eb1..e437d0d813 100644 --- a/Core/Core.vcxproj.filters +++ b/Core/Core.vcxproj.filters @@ -1237,6 +1237,9 @@ MIPS\RiscV + + MIPS\IR + @@ -1992,6 +1995,9 @@ MIPS\RiscV + + MIPS\IR + diff --git a/Core/MIPS/IR/IRAnalysis.cpp b/Core/MIPS/IR/IRAnalysis.cpp new file mode 100644 index 0000000000..0a8b861b89 --- /dev/null +++ b/Core/MIPS/IR/IRAnalysis.cpp @@ -0,0 +1,135 @@ +// Copyright (c) 2016- 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 "Core/MIPS/IR/IRAnalysis.h" + +static bool IRReadsFrom(const IRInst &inst, int reg, char type, bool directly = false) { + const IRMeta *m = GetIRMeta(inst.op); + + if (m->types[1] == type && inst.src1 == reg) { + return true; + } + if (m->types[2] == type && inst.src2 == reg) { + return true; + } + if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == type && inst.src3 == reg) { + return true; + } + if (!directly) { + if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) + return true; + if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) + return true; + } + return false; +} + +bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly) { + if (IRReadsFrom(inst, reg, 'F', directly)) + return true; + + const IRMeta *m = GetIRMeta(inst.op); + + // We also need to check V and 2. Indirect reads already checked, don't check again. + if (m->types[1] == 'V' && reg >= inst.src1 && reg < inst.src1 + 4) + return true; + if (m->types[1] == '2' && reg >= inst.src1 && reg < inst.src1 + 2) + return true; + if (m->types[2] == 'V' && reg >= inst.src2 && reg < inst.src2 + 4) + return true; + if (m->types[2] == '2' && reg >= inst.src2 && reg < inst.src2 + 2) + return true; + if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0) { + if (m->types[0] == 'V' && reg >= inst.src3 && reg <= inst.src3 + 4) + return true; + if (m->types[0] == '2' && reg >= inst.src3 && reg <= inst.src3 + 2) + return true; + } + return false; +} + +bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly) { + return IRReadsFrom(inst, reg, 'G', directly); +} + +int IRDestGPR(const IRInst &inst) { + const IRMeta *m = GetIRMeta(inst.op); + + if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') { + return inst.dest; + } + return -1; +} + +bool IRWritesToGPR(const IRInst &inst, int reg) { + return IRDestGPR(inst) == reg; +} + +bool IRWritesToFPR(const IRInst &inst, int reg) { + const IRMeta *m = GetIRMeta(inst.op); + + // Doesn't write to anything. + if ((m->flags & IRFLAG_SRC3) != 0) + return false; + + if (m->types[0] == 'F' && reg == inst.dest) + return true; + if (m->types[0] == 'V' && reg >= inst.dest && reg < inst.dest + 4) + return true; + if (m->types[0] == '2' && reg >= inst.dest && reg < inst.dest + 2) + return true; + return false; +} + +IRUsage IRNextGPRUsage(int gpr, const IRSituation &info) { + // Exclude any "special" regs from this logic for now. + if (gpr >= 32) + return IRUsage::UNKNOWN; + + int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount); + for (int i = 0; i < count; ++i) { + const IRInst inst = info.instructions[info.currentIndex + i]; + if (IRReadsFromGPR(inst, gpr)) + return IRUsage::READ; + // We say WRITE when the current instruction writes. It's not useful for spilling. + if (IRDestGPR(inst) == gpr) + return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED; + } + + return IRUsage::UNUSED; +} + +IRUsage IRNextFPRUsage(int fpr, const IRSituation &info) { + // Let's only pay attention to standard FP regs and temps. + // See MIPS.h for these offsets. + if (fpr < 0 || (fpr >= 160 && fpr < 192) || fpr >= 208) + return IRUsage::UNKNOWN; + + int count = std::min(info.numInstructions - info.currentIndex, info.lookaheadCount); + for (int i = 0; i < count; ++i) { + const IRInst inst = info.instructions[info.currentIndex + i]; + + if (IRReadsFromFPR(inst, fpr)) + return IRUsage::READ; + // We say WRITE when the current instruction writes. It's not useful for spilling. + if (IRWritesToFPR(inst, fpr)) { + return i == 0 ? IRUsage::WRITE : IRUsage::CLOBBERED; + } + } + + return IRUsage::UNUSED; +} diff --git a/Core/MIPS/IR/IRAnalysis.h b/Core/MIPS/IR/IRAnalysis.h new file mode 100644 index 0000000000..2b5ff598b4 --- /dev/null +++ b/Core/MIPS/IR/IRAnalysis.h @@ -0,0 +1,44 @@ +// Copyright (c) 2016- 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 "Core/MIPS/IR/IRInst.h" + +bool IRReadsFromFPR(const IRInst &inst, int reg, bool directly = false); +bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false); +bool IRWritesToGPR(const IRInst &inst, int reg); +bool IRWritesToFPR(const IRInst &inst, int reg); +int IRDestGPR(const IRInst &inst); + +struct IRSituation { + int lookaheadCount; + int currentIndex; + const IRInst *instructions; + int numInstructions; +}; + +enum class IRUsage { + UNKNOWN, + UNUSED, + READ, + WRITE, + CLOBBERED, +}; + +IRUsage IRNextGPRUsage(int gpr, const IRSituation &info); +IRUsage IRNextFPRUsage(int fpr, const IRSituation &info); diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index 738b2151f0..ff623776a3 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -1,6 +1,5 @@ #include "Common/CommonFuncs.h" #include "Core/MIPS/IR/IRInst.h" -#include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/MIPSDebugInterface.h" // Legend diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index 1253ca5061..039b8ee197 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -36,7 +36,6 @@ #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/IR/IRRegCache.h" #include "Core/MIPS/IR/IRJit.h" -#include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/Reporting.h" diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 11b8e5292c..9e0f12c587 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -6,6 +6,7 @@ #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" #include "Core/Config.h" +#include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/IR/IRInterpreter.h" #include "Core/MIPS/IR/IRPassSimplify.h" #include "Core/MIPS/IR/IRRegCache.h" @@ -803,27 +804,6 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts return logBlocks; } -bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false) { - const IRMeta *m = GetIRMeta(inst.op); - - if (m->types[1] == 'G' && inst.src1 == reg) { - return true; - } - if (m->types[2] == 'G' && inst.src2 == reg) { - return true; - } - if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == reg) { - return true; - } - if (!directly) { - if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement || inst.op == IROp::Syscall || inst.op == IROp::Break) - return true; - if (inst.op == IROp::Breakpoint || inst.op == IROp::MemoryCheck) - return true; - } - return false; -} - IRInst IRReplaceSrcGPR(const IRInst &inst, int fromReg, int toReg) { IRInst newInst = inst; const IRMeta *m = GetIRMeta(inst.op); @@ -840,15 +820,6 @@ IRInst IRReplaceSrcGPR(const IRInst &inst, int fromReg, int toReg) { return newInst; } -int IRDestGPR(const IRInst &inst) { - const IRMeta *m = GetIRMeta(inst.op); - - if ((m->flags & IRFLAG_SRC3) == 0 && m->types[0] == 'G') { - return inst.dest; - } - return -1; -} - IRInst IRReplaceDestGPR(const IRInst &inst, int fromReg, int toReg) { IRInst newInst = inst; const IRMeta *m = GetIRMeta(inst.op); diff --git a/Core/MIPS/MIPSAnalyst.cpp b/Core/MIPS/MIPSAnalyst.cpp index 4cfe9a2292..1cdeeb00ba 100644 --- a/Core/MIPS/MIPSAnalyst.cpp +++ b/Core/MIPS/MIPSAnalyst.cpp @@ -813,7 +813,7 @@ namespace MIPSAnalyst { break; } - if (reg > 32) { + if (reg >= 32) { return USAGE_UNKNOWN; } diff --git a/Core/MIPS/RiscV/RiscVJit.cpp b/Core/MIPS/RiscV/RiscVJit.cpp index bf3bf4e60a..a9724d1045 100644 --- a/Core/MIPS/RiscV/RiscVJit.cpp +++ b/Core/MIPS/RiscV/RiscVJit.cpp @@ -111,19 +111,20 @@ bool RiscVJit::CompileTargetBlock(IRBlock *block, int block_num, bool preload) { // TODO: Block linking, checked entries and such. - gpr.Start(); - fpr.Start(); + gpr.Start(block); + fpr.Start(block); for (int i = 0; i < block->GetNumInstructions(); ++i) { const IRInst &inst = block->GetInstructions()[i]; + gpr.SetIRIndex(i); + fpr.SetIRIndex(i); + CompileIRInst(inst); - if (jo.Disabled(JitDisable::REGALLOC_GPR)) { + if (jo.Disabled(JitDisable::REGALLOC_GPR)) gpr.FlushAll(); - } - if (jo.Disabled(JitDisable::REGALLOC_FPR)) { + if (jo.Disabled(JitDisable::REGALLOC_FPR)) fpr.FlushAll(); - } // Safety check, in case we get a bunch of really large jit ops without a lot of branching. if (GetSpaceLeft() < 0x800) { diff --git a/Core/MIPS/RiscV/RiscVRegCache.cpp b/Core/MIPS/RiscV/RiscVRegCache.cpp index 25f7b5db59..8885905f85 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.cpp +++ b/Core/MIPS/RiscV/RiscVRegCache.cpp @@ -21,6 +21,7 @@ #include "Common/CPUDetect.h" #include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/RiscV/RiscVRegCache.h" #include "Core/MIPS/JitCommon/JitState.h" #include "Core/Reporting.h" @@ -36,7 +37,7 @@ void RiscVRegCache::Init(RiscVEmitter *emitter) { emit_ = emitter; } -void RiscVRegCache::Start() { +void RiscVRegCache::Start(MIPSComp::IRBlock *irBlock) { if (!initialReady_) { SetupInitialRegs(); initialReady_ = true; @@ -56,6 +57,9 @@ void RiscVRegCache::Start() { mr[statics[i].mr].isStatic = true; mr[statics[i].mr].spillLock = true; } + + irBlock_ = irBlock; + irIndex_ = 0; } void RiscVRegCache::SetupInitialRegs() { @@ -362,8 +366,6 @@ 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? bool clobbered; RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); if (bestToSpill == INVALID_REG) { @@ -392,6 +394,12 @@ RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { static const int UNUSED_LOOKAHEAD_OPS = 30; + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + info.currentIndex = irIndex_; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + *clobbered = false; for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; @@ -401,16 +409,24 @@ RiscVReg RiscVRegCache::FindBestToSpill(bool unusedOnly, bool *clobbered) { continue; // As it's in alloc-order, we know it's not static so we don't need to check for that. + IRUsage usage = IRNextGPRUsage(ar[reg].mipsReg, info); - // TODO: Look for clobbering in the IRInst array with index? - - // Not awesome. A used reg. Let's try to avoid spilling. - // TODO: Actually check if we'd be spilling. - if (unusedOnly) { - continue; + // Awesome, a clobbered reg. Let's use it. + if (usage == IRUsage::CLOBBERED) { + // TODO: Check HI/LO clobber together if we combine. + bool canClobber = true; + if (canClobber) { + *clobbered = true; + return reg; + } } - return reg; + // Not awesome. A used reg. Let's try to avoid spilling. + if (!unusedOnly || usage == IRUsage::UNUSED) { + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + return reg; + } } return INVALID_REG; diff --git a/Core/MIPS/RiscV/RiscVRegCache.h b/Core/MIPS/RiscV/RiscVRegCache.h index 10305f5e25..ffe373c639 100644 --- a/Core/MIPS/RiscV/RiscVRegCache.h +++ b/Core/MIPS/RiscV/RiscVRegCache.h @@ -19,6 +19,7 @@ #include "Common/RiscVEmitter.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/IR/IRJit.h" namespace RiscVJitConstants { @@ -101,8 +102,10 @@ public: ~RiscVRegCache() {} void Init(RiscVGen::RiscVEmitter *emitter); - // TODO: Maybe pass in IR block and start PC for logging/debugging? - void Start(); + void Start(MIPSComp::IRBlock *irBlock); + void SetIRIndex(int index) { + irIndex_ = index; + } // Protect the arm register containing a MIPS register from spilling, to ensure that // it's being kept allocated. @@ -176,6 +179,8 @@ private: MIPSState *mips_; RiscVGen::RiscVEmitter *emit_ = nullptr; MIPSComp::JitOptions *jo_; + MIPSComp::IRBlock *irBlock_ = nullptr; + int irIndex_ = 0; enum { NUM_RVREG = 32, // 31 actual registers, plus the zero/sp register which is not mappable. diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp index 48d0400d1d..c99261b9f9 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.cpp @@ -20,6 +20,8 @@ #endif #include "Common/CPUDetect.h" +#include "Core/MIPS/IR/IRInst.h" +#include "Core/MIPS/IR/IRAnalysis.h" #include "Core/MIPS/RiscV/RiscVRegCacheFPU.h" #include "Core/MIPS/JitCommon/JitState.h" #include "Core/Reporting.h" @@ -34,7 +36,7 @@ void RiscVRegCacheFPU::Init(RiscVEmitter *emitter) { emit_ = emitter; } -void RiscVRegCacheFPU::Start() { +void RiscVRegCacheFPU::Start(MIPSComp::IRBlock *irBlock) { if (!initialReady_) { SetupInitialRegs(); initialReady_ = true; @@ -43,6 +45,9 @@ void RiscVRegCacheFPU::Start() { memcpy(ar, arInitial_, sizeof(ar)); memcpy(mr, mrInitial_, sizeof(mr)); pendingFlush_ = false; + + irBlock_ = irBlock; + irIndex_ = 0; } void RiscVRegCacheFPU::SetupInitialRegs() { @@ -130,8 +135,6 @@ 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? bool clobbered; RiscVReg bestToSpill = FindBestToSpill(true, &clobbered); if (bestToSpill == INVALID_REG) { @@ -160,21 +163,33 @@ RiscVReg RiscVRegCacheFPU::FindBestToSpill(bool unusedOnly, bool *clobbered) { static const int UNUSED_LOOKAHEAD_OPS = 30; + IRSituation info; + info.lookaheadCount = UNUSED_LOOKAHEAD_OPS; + info.currentIndex = irIndex_; + info.instructions = irBlock_->GetInstructions(); + info.numInstructions = irBlock_->GetNumInstructions(); + *clobbered = false; for (int i = 0; i < allocCount; i++) { RiscVReg reg = allocOrder[i]; if (ar[reg - F0].mipsReg != IRREG_INVALID && mr[ar[reg - F0].mipsReg].spillLock) continue; - // TODO: Look for clobbering in the IRInst array with index? + // As it's in alloc-order, we know it's not static so we don't need to check for that. + IRUsage usage = IRNextFPRUsage(ar[reg - F0].mipsReg, info); - // Not awesome. A used reg. Let's try to avoid spilling. - // TODO: Actually check if we'd be spilling. - if (unusedOnly) { - continue; + // Awesome, a clobbered reg. Let's use it. + if (usage == IRUsage::CLOBBERED) { + *clobbered = true; + return reg; } - return reg; + // Not awesome. A used reg. Let's try to avoid spilling. + if (!unusedOnly || usage == IRUsage::UNUSED) { + // TODO: Use age or something to choose which register to spill? + // TODO: Spill dirty regs first? or opposite? + return reg; + } } return INVALID_REG; diff --git a/Core/MIPS/RiscV/RiscVRegCacheFPU.h b/Core/MIPS/RiscV/RiscVRegCacheFPU.h index 30f897d43d..9edda169aa 100644 --- a/Core/MIPS/RiscV/RiscVRegCacheFPU.h +++ b/Core/MIPS/RiscV/RiscVRegCacheFPU.h @@ -46,8 +46,10 @@ public: ~RiscVRegCacheFPU() {} void Init(RiscVGen::RiscVEmitter *emitter); - // TODO: Maybe pass in IR block and start PC for logging/debugging? - void Start(); + void Start(MIPSComp::IRBlock *irBlock); + void SetIRIndex(int index) { + irIndex_ = index; + } // Protect the RISC-V register containing a MIPS register from spilling, to ensure that // it's being kept allocated. @@ -89,6 +91,8 @@ private: MIPSState *mips_; RiscVGen::RiscVEmitter *emit_ = nullptr; MIPSComp::JitOptions *jo_; + MIPSComp::IRBlock *irBlock_ = nullptr; + int irIndex_ = 0; enum { // On RiscV, each of the 32 registers are full 128-bit. No sharing of components! diff --git a/UWP/CoreUWP/CoreUWP.vcxproj b/UWP/CoreUWP/CoreUWP.vcxproj index ca53e7d496..5af1ee85d0 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj +++ b/UWP/CoreUWP/CoreUWP.vcxproj @@ -302,6 +302,7 @@ + @@ -561,6 +562,7 @@ + diff --git a/UWP/CoreUWP/CoreUWP.vcxproj.filters b/UWP/CoreUWP/CoreUWP.vcxproj.filters index f6ccb537f0..363504bba7 100644 --- a/UWP/CoreUWP/CoreUWP.vcxproj.filters +++ b/UWP/CoreUWP/CoreUWP.vcxproj.filters @@ -645,6 +645,9 @@ MIPS\IR + + MIPS\IR + MIPS\IR @@ -1667,6 +1670,9 @@ MIPS\IR + + MIPS\IR + MIPS\IR diff --git a/android/jni/Android.mk b/android/jni/Android.mk index 4fe1d0df45..494a4d6316 100644 --- a/android/jni/Android.mk +++ b/android/jni/Android.mk @@ -390,6 +390,7 @@ EXEC_AND_LIB_FILES := \ $(SRC)/Core/MIPS/MIPSVFPUFallbacks.cpp.arm \ $(SRC)/Core/MIPS/MIPSCodeUtils.cpp.arm \ $(SRC)/Core/MIPS/MIPSDebugInterface.cpp \ + $(SRC)/Core/MIPS/IR/IRAnalysis.cpp \ $(SRC)/Core/MIPS/IR/IRFrontend.cpp \ $(SRC)/Core/MIPS/IR/IRJit.cpp \ $(SRC)/Core/MIPS/IR/IRCompALU.cpp \ diff --git a/libretro/Makefile.common b/libretro/Makefile.common index 8c6a90ca06..70c00dfee2 100644 --- a/libretro/Makefile.common +++ b/libretro/Makefile.common @@ -671,6 +671,7 @@ SOURCES_CXX += \ $(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 \ $(COREDIR)/MIPS/IR/IRCompFPU.cpp \