This is an archive of the discontinued LLVM Phabricator instance.

[DebugInfo][InstrRef] Use correct (and existing) PHI placement utilities for machine locations
ClosedPublic

Authored by jmorse on Sep 21 2021, 7:00 AM.

Details

Summary

Over in D102158 I outlined some flaws in my LiveDebugValues-rewrite, and pointed out that in reality it was SSA-construction with value propagation. I've been re-formulating the implementation to be defined in terms of SSA construction, which is what this patch does. This patch addresses the first "problem" that's solved, finding which value numbers are in which locations in the MachineFunction. The existing LLVM iterated-dominance-frontier tooling is used for PHI placement. This eliminates the lattice arrangement I was trying to work with before.

This patch also adds a dependency on MachineDominatorTree for LiveDebugValues -- it's needed for the IDF calculator.

I find worked examples to be the best way of demonstrating how this works, thus this patch also adds a bunch of unit tests: different patterns of register definitions are tested over five or six weird control flow forms. They're all of the form:

  • Set-up where the definitions (or copies) of registers are in the function,
  • Run the PHI-placement / value-propagation function,
  • Test that the correct values are found in the correct locations.

I can't promise that they exhaustively covers all input patterns, but they illustrate what this code is _supposed_ to do, and it'll help check future changes.

Over on the excellent compile-time-tracker pages, this patch [0] causes a roughly 1% slowdown across CTMark, possibly because my previous implementation wasn't doing PHI placement correctly, but it's quite likely that the IDF calculator is slower than what I was doing. I reckon that this performance can be recovered by only running the PHI placement algorithm for register units, then placing PHIs for all aliasing registers too. I haven't tried this yet though.

I have a similar patch coming that does this again, but for the other "problem", what values should variables have in each block. (Still writing unit tests for that).

[0] http://llvm-compile-time-tracker.com/compare.php?from=be06359638e470418d57c1c2592d15c4e468daf1&to=42e0868898a534d4772ee5e369ef29dfb89c95bc&stat=instructions

Diff Detail

Event Timeline

jmorse created this revision.Sep 21 2021, 7:00 AM
jmorse requested review of this revision.Sep 21 2021, 7:00 AM
Herald added a project: Restricted Project. · View Herald TranscriptSep 21 2021, 7:00 AM

First pass looking through this, haven't gone through the unit test yet - I think I understand this roughly, and don't have any complaints, but will continue to review - 1 nit and 1 question enclosed.

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
1760–1762

My understanding of this is incomplete, under what circumstances would we have eliminated the PHI without having already assigned InLocs[Idx.asU64()] = FirstVal?

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.h
764

Return value documentation needs updating

First pass looking through this, haven't gone through the unit test yet - I think I understand this roughly, and don't have any complaints, but will continue to review - 1 nit and 1 question enclosed.

Huzzah!,

Reviewing the whole set of unit tests might be beyond the call of duty, but would be appreciated,

I'm trying to piece back together what I'd intended in the unit tests, and it looks like it'd be clearer if the transfer function was completely re-constructed before each call to buildMLocValueMap. Right now, I'm relying on earlier state, which makes it harder to localise and understand a single block of test.

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
1760–1762

Great question; the elimination of one PHI might enable the elimination of additional PHIs, possibly higher up the CFG. That could cause the live-in value on entry to a block to change more than once. If we had for example:

     |
  loop head-------\
     |            ^
     A            ^
   /   \          ^
  /     \         ^  
assign   B        ^ 
  \     /         ^    
   \   /          ^     
     C------------/
     |
   exit

Assume that we're dealing with $rax, which has some value prior to the loop head that I'll call "foo", and $rax is modified in the block labelled "assign". The PHI placement code isn't aware of what concrete values the register has, only that it's modified in the "assign" block. Therefore it installs two PHIs: at the 'C' block, and the loop head.

During value propagation, lets say that the "assign" block is found to not actually change the value of $rax: perhaps it modifies it, but then restores it from a stack spill slot. This means the PHI in block C can be eliminated: the live-in value is determined to be the PHI from "loop head"

Subsequently, we can identify that the PHI at "loop head" just feeds the value it generates in $rax back into itself on the backedge: that PHI can be eliminated too. We can then propagate the "foo" value into every block in the function. When we come to "C", there's no longer a PHI on entry to that block, but we still need to set "foo" as its live-in value for $rax. That's what this portion of code seeks to do.

Orlando accepted this revision.Sep 28 2021, 9:57 AM

haven't gone through the unit test yet

I focused my review on the unittest parts. I reviewed the tests closely up to and including MLocSimpleLoop, after which I started skimming a little (see the inline nit/comment about improving test readability).

Once both of our nits have been addressed, assuming @StephenTozer is happy with the code changes he reviewed, I think we can consider both halves of this patch reviewed. So, LGTM.

llvm/lib/CodeGen/LiveDebugValues/InstrRefBasedImpl.cpp
55

👍

llvm/unittests/CodeGen/InstrRefLDVTest.cpp
112

nit: missing a full stop.

119

Can you add an assert / llvm_unreachable?

232

nit: full stop.

288

LiveInRax is unused in this test. Is that intentional?

376

👍

427

nit: could you fix this comment?

480

nit: wobbly comment

487

👍

548

I think that the tests, especially this one, would be easier to read if there was a comment mapping the block numbers to the block names used in the CFG comment at the start of each test. Or, slightly more radically, replace the integer literals with a name. E.g.

const uint64_t Entry = 0, Loop1 = ...;
LiveInRsp(Entry, 0, RspLoc);
This revision is now accepted and ready to land.Sep 28 2021, 9:57 AM
jmorse updated this revision to Diff 377881.Oct 7 2021, 9:11 AM
jmorse marked 9 inline comments as done.

Mashed review comments into this patch

jmorse added a comment.Oct 7 2021, 9:13 AM

NB: I've also fiddled with the mloc tests so that they clear and rebuild the transfer function between each call to buildMLocValueMap, that way you don't have to keep "what's in the transfer function" in mind throughout.

llvm/unittests/CodeGen/InstrRefLDVTest.cpp
119

I think this is probably discouraged in unit tests, as it blocks all the other CodeGen tests from running; on the other hand, the test configuration is completely broken if it fires, so SGTM

288

Ah, yeah, this is a copy+paste error

548

Probably yes; although then it probably wouldn't all nicely align with fixed-width fonts then :(

I'll try to do this more in future tests, would rather not rewrite this one though.

This revision was landed with ongoing or failed builds.Oct 13 2021, 4:51 AM
This revision was automatically updated to reflect the committed changes.

NB, I did a little shuffling in the landed version: rather than asking for a MachineDominatorTree analysis, I'm keeping the object in the LiveDebugValues object, and building the dominator tree whenever LiveDebugValues is run in instr-ref mode.

The reason is that registering our use of the MachineDominatorTree analysis means it'll run whether or not we're in instruction referencing mode; needlessly putting an extra compile time cost on people not using instruction referencing. There's also (currently) no benefit from sharing analysis.

This could be changed in the future for efficiency, particularly when the new pass manager gets to the CodeGen backend.