mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
Merge pull request #15721 from unknownbrackets/ir-simplify
Add tests for IR and optimize to Mov a bit more
This commit is contained in:
10 files changed
+186
-7
No files matched your search
@@ -88,6 +88,7 @@ __testoutput.txt
|
||||
__testerror.txt
|
||||
__testfinish.txt
|
||||
__testfailure.bmp
|
||||
__testcompare.png
|
||||
GameLogNotes.txt
|
||||
screenshots
|
||||
android/assets/lang
|
||||
|
||||
@@ -2321,6 +2321,7 @@ if(UNITTEST)
|
||||
unittest/TestShaderGenerators.cpp
|
||||
unittest/TestArmEmitter.cpp
|
||||
unittest/TestArm64Emitter.cpp
|
||||
unittest/TestIRPassSimplify.cpp
|
||||
unittest/TestX64Emitter.cpp
|
||||
unittest/TestVertexJit.cpp
|
||||
unittest/TestSoftwareGPUJit.cpp
|
||||
|
||||
@@ -416,8 +416,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
|
||||
} else if (gpr.IsImm(inst.src2)) {
|
||||
const u32 imm2 = gpr.GetImm(inst.src2);
|
||||
gpr.MapDirtyIn(inst.dest, inst.src1);
|
||||
if (imm2 == 0 && (inst.op == IROp::Add || inst.op == IROp::Or)) {
|
||||
// Add / Or with zero is just a Mov.
|
||||
if (imm2 == 0 && (inst.op == IROp::Add || inst.op == IROp::Sub || inst.op == IROp::Or || inst.op == IROp::Xor)) {
|
||||
// Add / Sub / Or / Xor with zero is just a Mov. Add / Or are most common.
|
||||
if (inst.dest != inst.src1)
|
||||
out.Write(IROp::Mov, inst.dest, inst.src1);
|
||||
} else {
|
||||
@@ -426,8 +426,8 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
|
||||
} else if (symmetric && gpr.IsImm(inst.src1)) {
|
||||
const u32 imm1 = gpr.GetImm(inst.src1);
|
||||
gpr.MapDirtyIn(inst.dest, inst.src2);
|
||||
if (imm1 == 0 && (inst.op == IROp::Add || inst.op == IROp::Or)) {
|
||||
// Add / Or with zero is just a Mov.
|
||||
if (imm1 == 0 && (inst.op == IROp::Add || inst.op == IROp::Or || inst.op == IROp::Xor)) {
|
||||
// Add / Or / Xor with zero is just a Mov.
|
||||
if (inst.dest != inst.src2)
|
||||
out.Write(IROp::Mov, inst.dest, inst.src2);
|
||||
} else {
|
||||
@@ -467,6 +467,11 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
|
||||
gpr.SetImm(inst.dest, 0);
|
||||
} else if (gpr.IsImm(inst.src1)) {
|
||||
gpr.SetImm(inst.dest, Evaluate(gpr.GetImm(inst.src1), inst.constant, inst.op));
|
||||
} else if (inst.constant == 0 && (inst.op == IROp::AddConst || inst.op == IROp::SubConst || inst.op == IROp::OrConst || inst.op == IROp::XorConst)) {
|
||||
// Convert an Add/Sub/Or/Xor with a constant zero to a Mov (just like with reg zero.)
|
||||
gpr.MapDirtyIn(inst.dest, inst.src1);
|
||||
if (inst.dest != inst.src1)
|
||||
out.Write(IROp::Mov, inst.dest, inst.src1);
|
||||
} else {
|
||||
gpr.MapDirtyIn(inst.dest, inst.src1);
|
||||
goto doDefault;
|
||||
@@ -738,6 +743,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
|
||||
}
|
||||
}
|
||||
}
|
||||
gpr.FlushAll();
|
||||
return logBlocks;
|
||||
}
|
||||
|
||||
|
||||
@@ -27,8 +27,7 @@ IRRegCache::IRRegCache(IRWriter *ir) : ir_(ir) {
|
||||
|
||||
void IRRegCache::FlushAll() {
|
||||
for (int i = 0; i < TOTAL_MAPPABLE_MIPSREGS; i++) {
|
||||
//if (i < IRTEMP_0)
|
||||
Flush(i);
|
||||
Flush(i);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1462,7 +1462,7 @@ skip:
|
||||
}
|
||||
break;
|
||||
case 0x08: // addi
|
||||
case 0x09: // adiu
|
||||
case 0x09: // addiu
|
||||
info.hasRelevantAddress = true;
|
||||
info.relevantAddress = cpu->GetRegValue(0,MIPS_GET_RS(op))+((s16)(op & 0xFFFF));
|
||||
break;
|
||||
|
||||
@@ -734,6 +734,7 @@ ifeq ($(UNITTEST),1)
|
||||
LOCAL_MODULE := ppsspp_unittest
|
||||
LOCAL_SRC_FILES := \
|
||||
$(SRC)/unittest/JitHarness.cpp \
|
||||
$(SRC)/unittest/TestIRPassSimplify.cpp \
|
||||
$(SRC)/unittest/TestShaderGenerators.cpp \
|
||||
$(SRC)/unittest/TestSoftwareGPUJit.cpp \
|
||||
$(SRC)/unittest/TestThreadManager.cpp \
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
// Copyright (c) 2022- 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 <cstdio>
|
||||
#include <cstring>
|
||||
#include "Core/MIPS/IR/IRInst.h"
|
||||
#include "Core/MIPS/IR/IRPassSimplify.h"
|
||||
|
||||
struct IRVerification {
|
||||
const char *name;
|
||||
const std::vector<IRInst> input;
|
||||
const std::vector<IRInst> expected;
|
||||
const std::vector<IRPassFunc> passes;
|
||||
};
|
||||
|
||||
static void LogInstructions(const std::vector<IRInst> &insts) {
|
||||
for (size_t i = 0; i < insts.size(); ++i) {
|
||||
char buf[256];
|
||||
DisassembleIR(buf, sizeof(buf), insts[i]);
|
||||
printf(" %s\n", buf);
|
||||
}
|
||||
}
|
||||
|
||||
static bool VerifyPass(const IRVerification &v) {
|
||||
IRWriter in, out;
|
||||
IROptions opts{};
|
||||
opts.unalignedLoadStore = true;
|
||||
|
||||
for (const auto &inst : v.input)
|
||||
in.Write(inst);
|
||||
if (IRApplyPasses(v.passes.data(), v.passes.size(), in, out, opts)) {
|
||||
printf("%s FAILED: Unable to apply passes (or wanted to log)\n", v.name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<IRInst> actual = out.GetInstructions();
|
||||
if (actual.size() != v.expected.size()) {
|
||||
printf("%s FAILED: produced %d instructions, expected %d\n", v.name, (int)actual.size(), (int)v.expected.size());
|
||||
printf("Actual:\n");
|
||||
LogInstructions(actual);
|
||||
printf("Expected:\n");
|
||||
LogInstructions(v.expected);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < actual.size(); ++i) {
|
||||
if (memcmp(&v.expected[i], &actual[i], sizeof(IRInst)) != 0) {
|
||||
char actualBuf[256];
|
||||
DisassembleIR(actualBuf, sizeof(actualBuf), actual[i]);
|
||||
char expectedBuf[256];
|
||||
DisassembleIR(expectedBuf, sizeof(expectedBuf), v.expected[i]);
|
||||
|
||||
if (strcmp(expectedBuf, actualBuf) == 0) {
|
||||
// This means a field (like src2) was left set but isn't relevant. Ignore.
|
||||
continue;
|
||||
}
|
||||
|
||||
printf("%s FAILED: #%d expected '%s' but was '%s'", v.name, (int)i, expectedBuf, actualBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static const IRVerification tests[] = {
|
||||
{
|
||||
"SimplePurgeTemps",
|
||||
{
|
||||
{ IROp::Add, { IRTEMP_0 }, MIPS_REG_A0, MIPS_REG_A1 },
|
||||
{ IROp::Mov, { MIPS_REG_V0 }, IRTEMP_0 },
|
||||
{ IROp::Add, { IRTEMP_0 }, MIPS_REG_A2, MIPS_REG_A3 },
|
||||
{ IROp::Mov, { MIPS_REG_V1 }, IRTEMP_0 },
|
||||
},
|
||||
{
|
||||
{ IROp::Add, { MIPS_REG_V0 }, MIPS_REG_A0, MIPS_REG_A1 },
|
||||
{ IROp::Add, { MIPS_REG_V1 }, MIPS_REG_A2, MIPS_REG_A3 },
|
||||
},
|
||||
{ &PurgeTemps },
|
||||
},
|
||||
{
|
||||
"SwapClobberTemp",
|
||||
{
|
||||
{ IROp::Sub, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Mov, { MIPS_REG_S0 }, MIPS_REG_A0 },
|
||||
{ IROp::Slt, { MIPS_REG_A0 }, MIPS_REG_V0, MIPS_REG_V1 },
|
||||
},
|
||||
{
|
||||
{ IROp::Sub, { MIPS_REG_S0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Slt, { MIPS_REG_A0 }, MIPS_REG_V0, MIPS_REG_V1 },
|
||||
},
|
||||
{ &PurgeTemps },
|
||||
},
|
||||
{
|
||||
"DoubleClobberTemp",
|
||||
{
|
||||
{ IROp::Add, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Mov, { MIPS_REG_S0 }, MIPS_REG_A0 },
|
||||
{ IROp::Mov, { MIPS_REG_S0 }, MIPS_REG_A1 },
|
||||
},
|
||||
{
|
||||
{ IROp::Add, { MIPS_REG_S0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Mov, { MIPS_REG_A0 }, MIPS_REG_S0 },
|
||||
{ IROp::Mov, { MIPS_REG_S0 }, MIPS_REG_A1 },
|
||||
},
|
||||
{ &PurgeTemps },
|
||||
},
|
||||
{
|
||||
"SimplePropagateConstants",
|
||||
{
|
||||
{ IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 0x12340000 },
|
||||
{ IROp::OrConst, { MIPS_REG_A0 }, MIPS_REG_A0, 0, 0x00005678 },
|
||||
{ IROp::AddConst, { MIPS_REG_A1 }, MIPS_REG_A0, 0, 0 },
|
||||
{ IROp::AddConst, { MIPS_REG_A2 }, MIPS_REG_A0, 0, 0x00001111 },
|
||||
},
|
||||
{
|
||||
{ IROp::SetConst, { MIPS_REG_A0 }, 0, 0, 0x12345678 },
|
||||
{ IROp::SetConst, { MIPS_REG_A1 }, 0, 0, 0x12345678 },
|
||||
{ IROp::SetConst, { MIPS_REG_A2 }, 0, 0, 0x12346789 },
|
||||
},
|
||||
{ &PropagateConstants },
|
||||
},
|
||||
{
|
||||
// Needed for PurgeTemps optimizations to work.
|
||||
"OrToMov",
|
||||
{
|
||||
{ IROp::Sub, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Or, { MIPS_REG_S0 }, MIPS_REG_A0, MIPS_REG_ZERO },
|
||||
{ IROp::Add, { MIPS_REG_S1 }, MIPS_REG_A0, MIPS_REG_ZERO },
|
||||
{ IROp::OrConst, { MIPS_REG_S2 }, MIPS_REG_A0, 0, 0 },
|
||||
{ IROp::AddConst, { MIPS_REG_S3 }, MIPS_REG_A0, 0, 0 },
|
||||
},
|
||||
{
|
||||
{ IROp::Sub, { MIPS_REG_A0 }, MIPS_REG_A1, MIPS_REG_A2 },
|
||||
{ IROp::Mov, { MIPS_REG_S0 }, MIPS_REG_A0 },
|
||||
{ IROp::Mov, { MIPS_REG_S1 }, MIPS_REG_A0 },
|
||||
{ IROp::Mov, { MIPS_REG_S2 }, MIPS_REG_A0 },
|
||||
{ IROp::Mov, { MIPS_REG_S3 }, MIPS_REG_A0 },
|
||||
},
|
||||
{ &PropagateConstants },
|
||||
},
|
||||
};
|
||||
|
||||
bool TestIRPassSimplify() {
|
||||
InitIR();
|
||||
|
||||
for (const auto &test : tests) {
|
||||
if (!VerifyPass(test))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -756,6 +756,7 @@ bool TestArm64Emitter();
|
||||
bool TestX64Emitter();
|
||||
bool TestShaderGenerators();
|
||||
bool TestSoftwareGPUJit();
|
||||
bool TestIRPassSimplify();
|
||||
bool TestThreadManager();
|
||||
|
||||
TestItem availableTests[] = {
|
||||
@@ -774,6 +775,7 @@ TestItem availableTests[] = {
|
||||
TEST_ITEM(VFPUSinCos),
|
||||
TEST_ITEM(MathUtil),
|
||||
TEST_ITEM(Parsers),
|
||||
TEST_ITEM(IRPassSimplify),
|
||||
TEST_ITEM(Jit),
|
||||
TEST_ITEM(MatrixTranspose),
|
||||
TEST_ITEM(ParseLBN),
|
||||
|
||||
@@ -382,6 +382,7 @@
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|ARM'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|ARM'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="TestIRPassSimplify.cpp" />
|
||||
<ClCompile Include="TestShaderGenerators.cpp" />
|
||||
<ClCompile Include="TestSoftwareGPUJit.cpp" />
|
||||
<ClCompile Include="TestThreadManager.cpp" />
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
<ClCompile Include="TestShaderGenerators.cpp" />
|
||||
<ClCompile Include="TestThreadManager.cpp" />
|
||||
<ClCompile Include="TestSoftwareGPUJit.cpp" />
|
||||
<ClCompile Include="TestIRPassSimplify.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="JitHarness.h" />
|
||||
|
||||
Reference in new issue
Block a user