Current condition for spill instruction recognition in LiveDebugValues does not recognize case when register is spilled and killed in next instruction.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
Thanks! Couple of comments inline.
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
435 ↗ | (On Diff #126904) | please run your patch through clang-format to fix up the indentation |
441 ↗ | (On Diff #126904) | perhaps write this as auto NextI = std::next(MI); ? |
448 ↗ | (On Diff #126904) | This looks like it is *almost* the same code as on line 434. Would it make sense to factor this into a separate function or lambda? |
test/DebugInfo/X86/kill-after-spill.ll | ||
1 ↗ | (On Diff #126904) | This test is really long, could it be further reduced? |
4 ↗ | (On Diff #126904) | For Machine passes, it is usually preferable to write MIR tests. To convert this into MIR, run llc -stop-before=livedebugvalues -o - and then in the test do RUN: llc -run-pass=livedebugvalues |
I wasn't able to produce simpler test example. I've deleted some of IR instructions from previous test. Deletion of any other instruction from IR would bypass the problem.
Thank you for your comments.
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
432–434 ↗ | (On Diff #127167) | Probably simpler as return MO.isKill();. |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
451 ↗ | (On Diff #127172) | Isn't that a no-op? |
Hmm.. I just try to apply your patch to trunk, but the testcase doesn't parse for me. Do I need a specific revision of LLVM?
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
443 ↗ | (On Diff #127180) | whether |
test/DebugInfo/X86/kill-after-spill.mir | ||
1 ↗ | (On Diff #127180) | this now needs to go into test/DebugInfo/MIR/X86/kill-after-spill.mir |
Thanks, I can run the test now. Unfortunately it looks like the test is not yet sufficient (see inline comment).
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
451 ↗ | (On Diff #127172) | The test still passes when I comment out line 449-450. Could you add coverage for these two lines, too? |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
451 ↗ | (On Diff #127172) | I'm not sure what are you asking. Do you think I should add test coverage for this particular condition on 449. If you do, only what I could think of is changing MIR test example in order to hit condition on line 449. It would go something like this then: %r12d = MOV32rr %r15d MOV32mr %rbp, 1, %noreg, -48, %noreg, renamable %r15d :: (store 4 into %stack.0) %edi = MOV32rr killed %r12d Then this wouldn't be recognized as spill instruction but it could be mentioned in test that this is changed in testing purpose. Or you are thinking of dispatching condition on 449? I've tried to describe particular situation from MIR with conditions from lines 449 and 448. But this situation could be described as spill instruction after which we expect that copy operation is done and that spill register is killed in the next instruction. Said like that then we don't need condition from line 449. |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
445 ↗ | (On Diff #127317) | Perhaps add a comment here why we are skipping here? |
449 ↗ | (On Diff #127317) | Perhaps add a comment here why we are breaking out of the loop here? |
451 ↗ | (On Diff #127172) | I'm asking for 100% test coverage for the patch. If a patch is adding new functionality, that functionality should be tested, so (1) we know that it works as intended and (2) future generations of LLVM developers won't be tempted to remove it "because it has no effect (on the testsuite)". |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
452 ↗ | (On Diff #127317) | If I read the code correctly, it would identify *any* instruction that stores a register to the stack as a spill instruction. The lambda sets "Reg" in line 439 unconditionally and the following code just checks whether "Reg" is used and killed, but doesn't set it back to 0 if it is not, so line 455 would always return true. Was this the intent? If so, we wouldn't need to look at the next instruction at all. I wrote the code originally to be very conservative in the identification of a spill, perhaps too conservative. |
test/DebugInfo/X86/kill-after-spill.mir | ||
5 ↗ | (On Diff #127317) | Typo: instruction Also, please end the sentence with a full stop! |
I do not quite get it why the case when the register is killed in the subsequent instructions is so interesting.
Is this some kind of heuristic just to cover one specific scenario (then I think the summary, and future commit message should mention that, and also add some code comments describing what is going on here).
I mean, what makes this situation with a kill in the subsequent instruction different from a situation when there is one or more instructions in between, such as
A: spills register x B: an MI not using or defining x C: last use of x (kill x)
With you solution the result would differ depending on how instruction B and C are scheduled. That seems a little bit hacky to me, and it would at least be worth mentioning that the solution is some kind of heuristic (or that there is a technical debt left).
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
451 ↗ | (On Diff #128325) | Isn't it still weird to use Reg here. The getRegIfKilled call on line 439 can return false without setting Reg (due to the operand not being a use, or not even being a register). |
test/DebugInfo/MIR/X86/kill-after-spill.mir | ||
13 ↗ | (On Diff #128325) | typo: instruction |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
443 ↗ | (On Diff #128325) | In case of bundles this will move to the next instruction inside the bundle (unless it is the last instruction in the bundle)? Is perhaps the intent to look at the next BUNDLE instruction in case of MI being inside a bundle? auto NextI = std::next(MachineBasicBlock::const_iterator::getAtBundleBegin(MI.getIterator())); There could ofcourse be parallel uses of "Reg" in the same bundle, and the KILL marking could perhaps be on one of those instructions. So if MI is bundled, perhaps you need to analyse all the parallel instructions (and the instructions in the next bundle)? The situation when there is another instruction in the same bundle that defines "Reg" is perhaps also interesting when it comes to additional test cases. But in that case I guess there should be a KILL marking for "Reg" already in the spilling store instruction (or in the BUNDLE head). Some test cases involving bundles would not hurt. |
I agree that this is a solution to a specific case. I would be OK with it going in for now (after addressing the remaining review comments) with a FIXME comment added to provide a more comprehensive solution later, i.e. one that addresses bundles and kills in instructions further down the chain, as Bjorn suggested.
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
451 ↗ | (On Diff #128325) |
Yes, Reg can be undefined in that case. Perhaps it should be set to 0 in the lambda when it's not a use or a register. |
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
429 ↗ | (On Diff #128951) | I would prefer if the name of the lambda would reflect that we are returning a boolean quantity, i.e. whether the operand is a killed register or not. The register itself is returned as a side-effect, so something like 'isKilledReg()' instead of 'getKilledReg()' would sound a bit more consistent to me. |
443 ↗ | (On Diff #128951) | I don't believe we need the 'else' now. |
451 ↗ | (On Diff #128951) | We don't need to initialize RegNext here, I think. |
Ok, that is fine with me. The FIXME tells me that the current solution is a deliberate choice (without the FIXME the code looks incomplete, and it is hard to tell what kind of scenarios the author was aiming for). Thanks!
lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
429 ↗ | (On Diff #128951) | I'd still prefer to call the lambda 'isKilledReg()' but I leave it up to you. |
llvm/trunk/lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
429 | @wolfgangp hello, I have a question about that why the register of MI must be marked, "killed"? Considering the following case: MOV32mr $rsp, 1, $noreg, 8, $noreg, $r9d :: (store 4 into %stack.3) ... DBG_VALUE $rsp, 0, !"fuck", !DIExpression(DW_OP_plus_uconst, 8), debug-location !960; llvm-project/build_release/bin/test.cpp:0 line no:14 indirect ... MOVSDmr $rsp, 1, $noreg, 8, $noreg, killed renamable $xmm0 :: (store 8 into %stack.3) Because the r9d is not marked kill, the LLVM will not know the value of r9d is on %stack.3. Any ideas? |
llvm/trunk/lib/CodeGen/LiveDebugValues.cpp | ||
---|---|---|
429 | Note Bjorns comment at https://reviews.llvm.org/D41226#969343 asking something similar -- this code is mostly a heuristic to catch a common case, where a variable is spilt then restored over a function call for example. It's not guaranteed to catch everything, and we would end up having to follow multiple locations per variable if we did. (Shameless plug: my LiveDebugValues rewrite should get this right, see https://lists.llvm.org/pipermail/llvm-dev/2020-June/142368.html . I've paused landing it for a bit, but will continue shortly). |
@wolfgangp hello, I have a question about that why the register of MI must be marked, "killed"?
Considering the following case:
Because the r9d is not marked kill, the LLVM will not know the value of r9d is on %stack.3.
Any ideas?