This is an archive of the discontinued LLVM Phabricator instance.

[Mips][DSP] Fix physregs incorrectly marked as dead.
Needs ReviewPublic

Authored by mbrkusanin on May 30 2019, 5:15 AM.

Details

Summary

Implicit operands that use DSPControl register such as RDDSP and WRDSP are added
after InstrEmitter::EmitMachineNode determines which ones should be marked as
dead. This will add a manual check of these flags and correct them if necessary.

We also don't need removing of dead flags in LiveVariable analysis pass anymore.

Diff Detail

Event Timeline

mbrkusanin created this revision.May 30 2019, 5:15 AM

The changes can be seen in some functions in test CodeGen/Mips/dsp-r1.ll. Where we previously had:

CMPU_EQ_QB killed %4:gpr32, killed %3:gpr32, implicit-def dead $dspccond
%5:gpr32 = RDDSP 31, implicit undef $dsppos, implicit undef $dspscount, implicit undef $dspcarry, implicit undef $dspoutflag, implicit undef $dspccond

We now have:

CMPU_EQ_QB killed %4:gpr32, killed %3:gpr32, implicit-def $dspccond
%5:gpr32 = RDDSP 31, implicit $dsppos, implicit $dspscount, implicit $dspcarry, implicit $dspoutflag, implicit killed $dspccond

Is it possible to add tests to check the changes?

I would like to avoid having "undead" registers, which are marked dead but are semantically necessary, at any point. That means never calling setIsDead(false); as part of instruction selection, even if the flags are eventually correct after post-isel hooks run. The reason for this is that otherwise it's very confusing for anyone reading the code, or MIR dumps, to understand how it's supposed to work.

Why aren't these registers modeled precisely during isel?

mbrkusanin updated this revision to Diff 203134.Jun 5 2019, 6:49 AM

Added test.

I would like to avoid having "undead" registers, which are marked dead but are semantically necessary, at any point. That means never calling setIsDead(false); as part of instruction selection, even if the flags are eventually correct after post-isel hooks run.

The idea was to remove setIsDead(false); which was already in LiveVariables.cpp. This way flags are corrected earlier.
Other possible solution would be to mark them as always live. Another solution would be to leave rL272647 as is, which lets the test CodeGen/Mips/dsp-r1.ll pass with -verify-machineinstrs flag, but then we have wrong dead flags.

The reason for this is that otherwise it's very confusing for anyone reading the code, or MIR dumps, to understand how it's supposed to work.
Why aren't these registers modeled precisely during isel?

That is the main issue. Because some registers for rddsp or wrdsp are added in post isel hook, the previous check which looks at uses in glue chain does not see it. As for modeling the instruction, I don't see any possible way of adding these registers to .td for these instructions. Is there some other place that you know of or have any suggestion where that could be done? I would prefer that too.

mbrkusanin updated this revision to Diff 203177.Jun 5 2019, 9:24 AM

Updated test.

I think fundamentally, the problem here is that the registers in question aren't being handled consistently.

Looking briefly, at the IR level, the DSPCtrl registers are implicit: it's equivalent to some unknown location in memory. Then, during isel, some instructions acquire explicit references to those registers. Then, after isel, some other instructions acquire explicit references to those registers. This is fundamentally broken; you have to transition from modeling DSPCtrl as unknown memory to modeling it as an explicit set of registers at exactly one point, for all operations which refer to DSPCtrl. Otherwise the dependencies are wrong. This doesn't just affect the dead/undef markings; this could also have implications for scheduling. For example, if we don't model the dependency between wrdsp and pick.qb, we could schedule them in the wrong order.

Not sure where the best place to convert from arbitrary "memory" to specific registers is. Probably the simplest is to do it all in post-isel hooks?

Problem with handling everything in post-isel hooks is that it is after marking registers as dead. So we would need to mark every instruction that uses any part of DSPControl as having an post-isel hook and there are more than 100 instructions (including MicroMips variants).

mbrkusanin edited the summary of this revision. (Show Details)

All changes are now in code for Mips target. Only change in common code is in removing there lines in LiveVariables.cpp that are preceded by a FIXME comment. Also, tests that check other DSPControl registers are added.

Fixed comment typos.

So we would need to mark every instruction that uses any part of DSPControl as having an post-isel hook and there are more than 100 instructions

It should be possible to do this without too much boilerplate using a TSFlags bit; you can mark all the instructions that need this particular rewrite, and check the bit in processFunctionAfterISel, instead of explicitly listing all the opcodes. The Mips backend uses TSFlags for other instruction properties already; see, for example, IsCTI.

Marked all instructions that define part of DSPControl register with TSFlag named defsDSPCtrl. This helps us avoid running unnecessary code on instructions that don't need it.

While we could add something similar for RDDSP and WRDSP instructions to make sure all implicit operands are added (which appears that it needs to be done in common CodeGen not Mips) before dead flag are set, it would still not help. This is because other instructions that use DSPControl are not glued to RDDSP and/or WRDSP. So we would still need to check for those instructions some other way which would not be any more efficient then current solution.

efriedma added inline comments.Jun 26 2019, 12:07 PM
lib/Target/Mips/MipsSEISelDAGToDAG.cpp
122

I'm still not really happy with the setIsDead(false), but I guess it's not a big deal if we aren't actually depending on those register defs for correct scheduling. Could probably use a comment explaining that, though.

133

Does this setIsDead() call actually run for some testcase? I don't see any dead flag in your testcase.

mbrkusanin marked an inline comment as done.
mbrkusanin edited the summary of this revision. (Show Details)
  • Added a comment in: removeOperandFromDeadImplRegs()
  • Removed MO.setIsDead() from markOperandInDeadImplRegs() and renamed to checkOperandForDeadImplRegs().
  • Added a comment in: checkOperandForDeadImplRegs().
  • Decided to remove the change from addDSPCtrlRegOperands() which marked RDDSP operands as implicit undef. Machine verifier would fail if only RDDSP was used without some other instruction previously defining a register. We could handle these Undef flags the same way we handle dead flags for other instructions (when we remove dead from previous we can also remove undef from current), but I don't think we need to touch these flags.
mbrkusanin marked an inline comment as done.Jun 27 2019, 9:05 AM
mbrkusanin marked an inline comment as done.Jun 27 2019, 9:11 AM
mbrkusanin added inline comments.
lib/Target/Mips/MipsSEISelDAGToDAG.cpp
133

You're right. We don't need it anymore. This was needed in an older version of the patch. Now we can just check if it's dead.

Machine verifier would fail if only RDDSP was used without some other instruction previously defining a register

If you're going to allow calling llvm.mips.rddsp without some other instruction defining the register, you need to mark the registers live-in to the function. Marking them "undef" means you aren't using the value in the register, which could cause issues later.

RKSimon resigned from this revision.Jul 2 2019, 4:14 AM
  • Marked undef DSPControl registers as live-in to the function and basic block.

Sorry for the delay. I was away for a while.