diff --git a/Core/MIPS/IR/IRInst.cpp b/Core/MIPS/IR/IRInst.cpp index b44d6704d7..9bf8fee367 100644 --- a/Core/MIPS/IR/IRInst.cpp +++ b/Core/MIPS/IR/IRInst.cpp @@ -131,6 +131,12 @@ static const IRMeta irMeta[] = { { IROp::FMovToGPR, "FMovToGPR", "GF" }, { IROp::OptFMovToGPRShr8, "OptFMovToGPRShr8", "GF" }, { IROp::OptFCvtSWFromGPR, "OptFCvtSWFromGPR", "FG" }, + { IROp::OptExitToConstIfEqElse, "OptExitIfEqElse", "CGG", IRFLAG_EXIT }, + { IROp::OptExitToConstIfNeqElse, "OptExitIfNeqElse", "CGG", IRFLAG_EXIT }, + { IROp::OptExitToConstIfGtZElse, "OptExitIfGtZElse", "CG", IRFLAG_EXIT }, + { IROp::OptExitToConstIfGeZElse, "OptExitIfGeZElse", "CG", IRFLAG_EXIT }, + { IROp::OptExitToConstIfLtZElse, "OptExitIfLtZElse", "CG", IRFLAG_EXIT }, + { IROp::OptExitToConstIfLeZElse, "OptExitIfLeZElse", "CG", IRFLAG_EXIT }, { IROp::FpCondFromReg, "FpCondFromReg", "_G" }, { IROp::FpCondToReg, "FpCondToReg", "G" }, { IROp::FpCtrlFromReg, "FpCtrlFromReg", "_G" }, diff --git a/Core/MIPS/IR/IRInst.h b/Core/MIPS/IR/IRInst.h index 55b9bd7473..cc392e2a84 100644 --- a/Core/MIPS/IR/IRInst.h +++ b/Core/MIPS/IR/IRInst.h @@ -141,6 +141,14 @@ enum class IROp : uint8_t { OptFCvtSWFromGPR, FMovToGPR, OptFMovToGPRShr8, + // A conditional exit merged with the ExitToConst after it, which stays behind as the target + // to take otherwise (in its constant). + OptExitToConstIfEqElse, + OptExitToConstIfNeqElse, + OptExitToConstIfGtZElse, + OptExitToConstIfGeZElse, + OptExitToConstIfLtZElse, + OptExitToConstIfLeZElse, FSat0_1, FSatMinus1_1, diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index 42fea35a5c..abaaf16d8f 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -1034,6 +1034,19 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst) { case IROp::ExitToReg: return mips->r[inst->src1]; + // The next instruction is the ExitToConst to take otherwise, never run itself. + case IROp::OptExitToConstIfEqElse: + return mips->r[inst->src1] == mips->r[inst->src2] ? inst->constant : inst[1].constant; + case IROp::OptExitToConstIfNeqElse: + return mips->r[inst->src1] != mips->r[inst->src2] ? inst->constant : inst[1].constant; + case IROp::OptExitToConstIfGtZElse: + return (s32)mips->r[inst->src1] > 0 ? inst->constant : inst[1].constant; + case IROp::OptExitToConstIfGeZElse: + return (s32)mips->r[inst->src1] >= 0 ? inst->constant : inst[1].constant; + case IROp::OptExitToConstIfLtZElse: + return (s32)mips->r[inst->src1] < 0 ? inst->constant : inst[1].constant; + case IROp::OptExitToConstIfLeZElse: + return (s32)mips->r[inst->src1] <= 0 ? inst->constant : inst[1].constant; case IROp::ExitToConstIfEq: if (mips->r[inst->src1] == mips->r[inst->src2]) return inst->constant; diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index 858fccdba7..11d3059dde 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -2407,6 +2407,19 @@ bool OptimizeForInterpreter(const IRWriter &in, IRWriter &out, const IROptions & } out.Write(inst); break; + case IROp::ExitToConstIfEq: + case IROp::ExitToConstIfNeq: + case IROp::ExitToConstIfGtZ: + case IROp::ExitToConstIfGeZ: + case IROp::ExitToConstIfLtZ: + case IROp::ExitToConstIfLeZ: + // With an ExitToConst right after, one op can pick either target. The ExitToConst stays + // as the second target's holder. + static_assert((int)IROp::OptExitToConstIfLeZElse - (int)IROp::OptExitToConstIfEqElse == (int)IROp::ExitToConstIfLeZ - (int)IROp::ExitToConstIfEq, "Else exits must match the exits' order"); + if (!last && in.GetInstructions()[i + 1].op == IROp::ExitToConst) + inst.op = (IROp)((int)IROp::OptExitToConstIfEqElse + ((int)inst.op - (int)IROp::ExitToConstIfEq)); + out.Write(inst); + break; default: out.Write(inst); break; diff --git a/unittest/TestIRPassSimplify.cpp b/unittest/TestIRPassSimplify.cpp index b40e3468d8..43669d3c63 100644 --- a/unittest/TestIRPassSimplify.cpp +++ b/unittest/TestIRPassSimplify.cpp @@ -510,6 +510,19 @@ static const IRVerification tests[] = { }, { &OptimizeLoadsAfterStores }, }, + { + "InterpreterExitElse", + { + { IROp::ExitToConstIfLtZ, { 0 }, MIPS_REG_A0, 0, 0x08804000 }, + { IROp::ExitToConst, { 0 }, 0, 0, 0x08804100 }, + }, + { + { IROp::Downcount, { 0 }, 0, 0, 0 }, + { IROp::OptExitToConstIfLtZElse, { 0 }, MIPS_REG_A0, 0, 0x08804000 }, + { IROp::ExitToConst, { 0 }, 0, 0, 0x08804100 }, + }, + { &OptimizeForInterpreter }, + }, }; bool TestIRPassSimplify() {