Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

irjit: Correct another PurgeTemps case #15739

Merged
merged 2 commits into from
Jul 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 39 additions & 7 deletions Core/MIPS/IR/IRPassSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,7 +747,7 @@ bool PropagateConstants(const IRWriter &in, IRWriter &out, const IROptions &opts
return logBlocks;
}

bool IRReadsFromGPR(const IRInst &inst, int reg) {
bool IRReadsFromGPR(const IRInst &inst, int reg, bool directly = false) {
const IRMeta *m = GetIRMeta(inst.op);

if (m->types[1] == 'G' && inst.src1 == reg) {
Expand All @@ -759,8 +759,11 @@ bool IRReadsFromGPR(const IRInst &inst, int reg) {
if ((m->flags & (IRFLAG_SRC3 | IRFLAG_SRC3DST)) != 0 && m->types[0] == 'G' && inst.src3 == reg) {
return true;
}
if (inst.op == IROp::Interpret || inst.op == IROp::CallReplacement) {
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;
}
Expand Down Expand Up @@ -810,16 +813,22 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
std::vector<IRInst> insts;
insts.reserve(in.GetInstructions().size());

// We track writes both to rename regs and to purge dead stores.
struct Check {
Check(int r, int i, bool rbx) : reg(r), index(i), readByExit(rbx) {
}

// Register this instruction wrote to.
int reg;
// Only other than -1 when it's a Mov, equivalent reg at this point.
int srcReg = -1;
// Index into insts for this op.
int index;
// Whether the dest reg is read by any Exit.
bool readByExit;
};
std::vector<Check> checks;
// This tracks the last index at which each reg was modified.
int lastWrittenTo[256];
memset(lastWrittenTo, -1, sizeof(lastWrittenTo));

Expand All @@ -828,23 +837,43 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
IRInst inst = in.GetInstructions()[i];
const IRMeta *m = GetIRMeta(inst.op);

// Check if we can optimize by running through all the writes we've previously found.
for (Check &check : checks) {
if (check.reg == 0) {
// This means we already optimized this or a later inst depends on it.
continue;
}

if (IRReadsFromGPR(inst, check.reg)) {
// Read from, but was this just a copy?
// If this reads from the reg, we either depend on it or we can fold or swap.
// That's determined below.

// If this reads and writes the reg (e.g. MovZ, Load32Left), we can't just swap.
bool mutatesReg = IRMutatesDestGPR(inst, check.reg);
bool cannotReplace = inst.op == IROp::Interpret || inst.op == IROp::CallReplacement;
// If this doesn't directly read (i.e. Interpret), we can't swap.
bool cannotReplace = !IRReadsFromGPR(inst, check.reg, true);
if (!mutatesReg && !cannotReplace && check.srcReg >= 0 && lastWrittenTo[check.srcReg] < check.index) {
// Replace with the srcReg instead. This happens with non-nice delay slots.
// We're changing "Mov A, B; Add C, C, A" to "Mov A, B; Add C, C, B" here.
// srcReg should only be set when it was a Mov.
inst = IRReplaceSrcGPR(inst, check.reg, check.srcReg);

// If the Mov modified the same reg as this instruction, we can't optimize from it anymore.
if (inst.dest == check.reg) {
check.reg = 0;
// We can also optimize it out since we've essentially moved now.
insts[check.index].op = IROp::Mov;
insts[check.index].dest = 0;
insts[check.index].src1 = 0;
}
} else if (!IRMutatesDestGPR(insts[check.index], check.reg) && inst.op == IROp::Mov && i == check.index + 1) {
// As long as the previous inst wasn't modifying its dest reg, and this is a Mov, we can swap.
// We're changing "Add A, B, C; Mov B, A" to "Add B, B, C; Mov A, B" here.

// This happens with lwl/lwr temps. Replace the original dest.
insts[check.index] = IRReplaceDestGPR(insts[check.index], check.reg, inst.dest);
lastWrittenTo[inst.dest] = check.index;
// If it's being read from (by inst), we can't optimize out.
// If it's being read from (by inst now), we can't optimize out.
check.reg = 0;
// Update the read by exit flag to match the new reg.
check.readByExit = inst.dest < IRTEMP_0 || inst.dest > IRTEMP_LR_SHIFT;
Expand All @@ -855,6 +884,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
check.reg = 0;
}
} else if (check.readByExit && (m->flags & IRFLAG_EXIT) != 0) {
// This is an exit, and the reg is read by any exit. Clear it.
check.reg = 0;
} else if (IRDestGPR(inst) == check.reg) {
// Clobbered, we can optimize out.
Expand All @@ -878,7 +908,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
case IRTEMP_LR_VALUE:
case IRTEMP_LR_MASK:
case IRTEMP_LR_SHIFT:
// Unlike other ops, these don't need to persist between blocks.
// Unlike other registers, these don't need to persist between blocks.
// So we consider them not read unless proven read.
lastWrittenTo[dest] = i;
// If this is a copy, we might be able to optimize out the copy.
Expand Down Expand Up @@ -911,6 +941,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
insts.push_back(inst);
}

// Since we're done with the instructions, all remaining can be nuked.
for (Check &check : checks) {
if (!check.readByExit && check.reg > 0) {
insts[check.index].op = IROp::Mov;
Expand All @@ -920,6 +951,7 @@ bool PurgeTemps(const IRWriter &in, IRWriter &out, const IROptions &opts) {
}

for (const IRInst &inst : insts) {
// Simply skip any Mov 0, 0 instructions, since that's how we nuke one.
if (inst.op != IROp::Mov || inst.dest != 0 || inst.src1 != 0) {
out.Write(inst);
}
Expand Down
13 changes: 13 additions & 0 deletions unittest/TestIRPassSimplify.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,19 @@ static const IRVerification tests[] = {
},
{ &PurgeTemps },
},
{
"Load32LeftPurgeTemps",
{
{ IROp::Mov, { IRTEMP_LR_ADDR }, MIPS_REG_A0 },
{ IROp::AndConst, { IRTEMP_LR_ADDR }, IRTEMP_LR_ADDR, 0, 0xFFFFFFFC },
{ IROp::Load32, { MIPS_REG_V0 }, IRTEMP_LR_ADDR, 0, 0 },
},
{
{ IROp::AndConst, { IRTEMP_LR_ADDR }, MIPS_REG_A0, 0, 0xFFFFFFFC },
{ IROp::Load32, { MIPS_REG_V0 }, IRTEMP_LR_ADDR, 0, 0 },
},
{ &PurgeTemps },
},
{
"SwapClobberTemp",
{
Expand Down