diff --git a/Core/MIPS/IR/IRPassSimplify.cpp b/Core/MIPS/IR/IRPassSimplify.cpp index d6f3bd5887..09dcc97f5f 100644 --- a/Core/MIPS/IR/IRPassSimplify.cpp +++ b/Core/MIPS/IR/IRPassSimplify.cpp @@ -627,6 +627,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::MovZ: case IROp::MovNZ: gpr.MapInInIn(inst.dest, inst.src1, inst.src2); + // The dest is read, then maybe written. + gpr.MapDirty(inst.dest); goto doDefault; case IROp::Min: @@ -664,11 +666,16 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::Store32Left: case IROp::Store32Right: case IROp::Store32Conditional: - if (gpr.IsImm(inst.src1) && inst.src1 != inst.dest) { + if (gpr.IsImm(inst.src1)) { gpr.MapIn(inst.dest); + // sc also writes its value reg, with the result. + if (inst.op == IROp::Store32Conditional) + gpr.MapDirty(inst.dest); out.Write(inst.op, inst.dest, 0, 0, gpr.GetImm(inst.src1) + inst.constant); } else { gpr.MapInIn(inst.dest, inst.src1); + if (inst.op == IROp::Store32Conditional) + gpr.MapDirty(inst.dest); goto doDefault; } break; @@ -688,9 +695,11 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::Load16Ext: case IROp::Load32: case IROp::Load32Linked: - if (gpr.IsImm(inst.src1) && inst.src1 != inst.dest) { + if (gpr.IsImm(inst.src1)) { + // Read the address first, as the dest may be the base (lui v0, hi; lw v0, lo(v0)). + u32 addr = gpr.GetImm(inst.src1) + inst.constant; gpr.MapDirty(inst.dest); - out.Write(inst.op, inst.dest, 0, 0, gpr.GetImm(inst.src1) + inst.constant); + out.Write(inst.op, inst.dest, 0, 0, addr); } else { gpr.MapDirtyIn(inst.dest, inst.src1); goto doDefault; @@ -707,11 +716,15 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts break; case IROp::Load32Left: case IROp::Load32Right: + // These merge into the dest, so it's read, then written. if (gpr.IsImm(inst.src1)) { + u32 addr = gpr.GetImm(inst.src1) + inst.constant; gpr.MapIn(inst.dest); - out.Write(inst.op, inst.dest, 0, 0, gpr.GetImm(inst.src1) + inst.constant); + gpr.MapDirty(inst.dest); + out.Write(inst.op, inst.dest, 0, 0, addr); } else { gpr.MapInIn(inst.dest, inst.src1); + gpr.MapDirty(inst.dest); goto doDefault; } break; @@ -773,6 +786,11 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::FLog2: case IROp::FHalfToFloat: case IROp::FSinCos: + case IROp::FMin: + case IROp::FMax: + case IROp::FSign: + case IROp::FSat0_1: + case IROp::FSatMinus1_1: out.Write(inst); break; @@ -837,6 +855,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts case IROp::Vec2Pack32To16: case IROp::Vec4Unpack8To32: case IROp::Vec2Unpack16To32: + case IROp::Vec2Unpack16To31: + case IROp::Vec2Pack31To16: case IROp::Vec4DuplicateUpperBitsAndShift1: out.Write(inst); break; @@ -885,7 +905,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts } break; } - gpr.FlushAll(); + // Only exits when taken, so the values stay known after. + gpr.FlushAll(true); goto doDefault; case IROp::ExitToConstIfGtZ: @@ -909,7 +930,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts } break; } - gpr.FlushAll(); + // Only exits when taken, so the values stay known after. + gpr.FlushAll(true); goto doDefault; case IROp::ExitToConst: @@ -944,6 +966,9 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts { gpr.FlushAll(); doDefault: + // Whatever the op writes isn't known anymore (its inputs were written out above). + if (GetIRMeta(inst.op)->types[0] == 'G' && (GetIRMeta(inst.op)->flags & IRFLAG_SRC3) == 0) + gpr.MapDirty(inst.dest); out.Write(inst); break; } diff --git a/Core/MIPS/IR/IRRegCache.cpp b/Core/MIPS/IR/IRRegCache.cpp index b5a544b5f6..957e7ea669 100644 --- a/Core/MIPS/IR/IRRegCache.cpp +++ b/Core/MIPS/IR/IRRegCache.cpp @@ -30,13 +30,10 @@ #include "Core/MIPS/JitCommon/JitState.h" void IRImmRegCache::Flush(IRReg rd) { - if (isImm_[rd]) { - if (rd == 0) { - return; - } + if (isImm_[rd] && !isWritten_[rd]) { _assert_((rd > 0 && rd < 32) || (rd >= IRTEMP_0 && rd < IRREG_VFPU_CTRL_BASE)); ir_->WriteSetConstant(rd, immVal_[rd]); - isImm_[rd] = false; + isWritten_[rd] = true; } } @@ -49,19 +46,23 @@ void IRImmRegCache::Discard(IRReg rd) { IRImmRegCache::IRImmRegCache(IRWriter *ir) : ir_(ir) { memset(&isImm_, 0, sizeof(isImm_)); + memset(&isWritten_, 0, sizeof(isWritten_)); memset(&immVal_, 0, sizeof(immVal_)); isImm_[0] = true; + isWritten_[0] = true; ir_ = ir; } -void IRImmRegCache::FlushAll() { +void IRImmRegCache::FlushAll(bool keepKnown) { for (int i = 1; i < TOTAL_MAPPABLE_IRREGS; ) { if (isImm_[i]) { Flush(i); + if (!keepKnown) + isImm_[i] = false; } // Most of the time, lots are not. This speeds it up a lot. - bool *next = (bool *)memchr(&isImm_[i], 1, TOTAL_MAPPABLE_IRREGS - i); + bool *next = (bool *)memchr(&isImm_[i + 1], 1, TOTAL_MAPPABLE_IRREGS - i - 1); if (!next) break; i = (int)(next - &isImm_[0]); @@ -88,18 +89,14 @@ void IRImmRegCache::MapInInIn(IRReg rd, IRReg rs, IRReg rt) { } void IRImmRegCache::MapDirtyIn(IRReg rd, IRReg rs) { - if (rs != rd) { - Discard(rd); - } Flush(rs); + Discard(rd); } void IRImmRegCache::MapDirtyInIn(IRReg rd, IRReg rs, IRReg rt) { - if (rs != rd && rt != rd) { - Discard(rd); - } Flush(rs); Flush(rt); + Discard(rd); } IRNativeRegCacheBase::IRNativeRegCacheBase(MIPSComp::JitOptions *jo) diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index c4d8688388..0df77b3a3e 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -49,6 +49,9 @@ public: IRImmRegCache(IRWriter *ir); void SetImm(IRReg r, u32 immVal) { + // Setting the value it already holds doesn't need writing again. + if (!isImm_[r] || immVal_[r] != immVal) + isWritten_[r] = false; isImm_[r] = true; immVal_[r] = immVal; } @@ -56,8 +59,12 @@ public: bool IsImm(IRReg r) const { return isImm_[r]; } u32 GetImm(IRReg r) const { return immVal_[r]; } - void FlushAll(); + // Writes out all pending values. With keepKnown, the values stay known afterward (for a + // conditional exit), otherwise they're forgotten (anything may change them after a barrier). + void FlushAll(bool keepKnown = false); + // The Map functions write out the values of the regs read, which stay known. A dirty reg + // is forgotten after its inputs are written. void MapDirty(IRReg rd); void MapIn(IRReg rd); void MapInIn(IRReg rs, IRReg rt); @@ -70,6 +77,8 @@ private: void Discard(IRReg rd); bool isImm_[TOTAL_MAPPABLE_IRREGS]; + // Whether the known value has been written to the reg. + bool isWritten_[TOTAL_MAPPABLE_IRREGS]; uint32_t immVal_[TOTAL_MAPPABLE_IRREGS]; IRWriter *ir_; }; diff --git a/unittest/TestIRPassSimplify.cpp b/unittest/TestIRPassSimplify.cpp index 4f3ad0ea09..4645d80c22 100644 --- a/unittest/TestIRPassSimplify.cpp +++ b/unittest/TestIRPassSimplify.cpp @@ -322,6 +322,88 @@ static const IRVerification tests[] = { false, true, }, + { + "PropagateConstantsPastFSat", + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 0x100 }, + { IROp::FSat0_1, { 1 }, 1 }, + { IROp::Add, { MIPS_REG_A1 }, MIPS_REG_A0, MIPS_REG_A0 }, + }, + { + { IROp::FSat0_1, { 1 }, 1 }, + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 0x100 }, + { IROp::SetConst, { MIPS_REG_A1 }, 0, 0, 0x200 }, + }, + { &PropagateConstants }, + }, + { + // lui v0, hi; lw v0, lo(v0) + "PropagateConstantsLoadIntoBase", + { + { IROp::SetConst, { MIPS_REG_V0 }, 0, 0, 0x08810000 }, + { IROp::Load32, { MIPS_REG_V0 }, MIPS_REG_V0, 0, 0x20 }, + }, + { + { IROp::Load32, { MIPS_REG_V0 }, MIPS_REG_ZERO, 0, 0x08810020 }, + }, + { &PropagateConstants }, + }, + { + // The store writes a0 out, and it stays known. + "PropagateConstantsPastStore", + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Store32, { MIPS_REG_A0 }, MIPS_REG_SP, 0, 0 }, + { IROp::AddConst, { MIPS_REG_A1 }, MIPS_REG_A0, 0, 0x20 }, + }, + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Store32, { MIPS_REG_A0 }, MIPS_REG_SP, 0, 0 }, + { IROp::SetConst, { MIPS_REG_A1 }, 0, 0, 0x25 }, + }, + { &PropagateConstants }, + }, + { + "PropagateConstantsPastCondExit", + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::ExitToConstIfEq, { 0 }, MIPS_REG_A1, MIPS_REG_A2, 0x08804000 }, + { IROp::AddConst, { MIPS_REG_A3 }, MIPS_REG_A0, 0, 1 }, + }, + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::ExitToConstIfEq, { 0 }, MIPS_REG_A1, MIPS_REG_A2, 0x08804000 }, + { IROp::SetConst, { MIPS_REG_A3 }, 0, 0, 6 }, + }, + { &PropagateConstants }, + }, + { + // Each of these may change a0 after reading it. + "PropagateConstantsReadThenWritten", + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::MovZ, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 }, + { IROp::AddConst, { MIPS_REG_T0 }, MIPS_REG_A0, 0, 1 }, + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Store32Conditional, { MIPS_REG_A0 }, MIPS_REG_A1, 0, 0 }, + { IROp::AddConst, { MIPS_REG_T1 }, MIPS_REG_A0, 0, 1 }, + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Load32Left, { MIPS_REG_A0 }, MIPS_REG_A1, 0, 3 }, + { IROp::AddConst, { MIPS_REG_T2 }, MIPS_REG_A0, 0, 1 }, + }, + { + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::MovZ, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 }, + { IROp::AddConst, { MIPS_REG_T0 }, MIPS_REG_A0, 0, 1 }, + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Store32Conditional, { MIPS_REG_A0 }, MIPS_REG_A1, 0, 0 }, + { IROp::AddConst, { MIPS_REG_T1 }, MIPS_REG_A0, 0, 1 }, + { IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 5 }, + { IROp::Load32Left, { MIPS_REG_A0 }, MIPS_REG_A1, 0, 3 }, + { IROp::AddConst, { MIPS_REG_T2 }, MIPS_REG_A0, 0, 1 }, + }, + { &PropagateConstants }, + }, }; bool TestIRPassSimplify() {