This is an archive of the discontinued LLVM Phabricator instance.

[DebugInfo][InstrRef] Don't break up return-sequences on debug-info instructions
ClosedPublic

Authored by jmorse on Jul 23 2021, 6:27 AM.

Details

Summary

When we have a terminator sequence (i.e. a tailcall or return), MIIsInTerminatorSequence is used to work out where the preceding ABI-setup instructions end, i.e. the parts that were glued to the terminator instruction. This allows LLVM to split blocks safely without having to worry about ABI stuff.

The function only ignores DBG_VALUE instructions, meaning that the two debug instructions I recently added can end terminator sequences early, causing various MachineVerifier errors. This patch promotes the test for debug instructions from "isDebugValue" to "isDebugInstr", thus avoiding any debug-info interfering with this function.

Unfortunately I don't have a test where I can replicate this: it would require getting ScheduleDAGSDNodes to emit debug instrs between a tail call and the instrs glued to it. This is happening on a large codebase (that I can't directly access) deep inside LTO, and this change fixes it. Hopefully everyone agrees: this is an obvious fix.

Diff Detail

Event Timeline

jmorse created this revision.Jul 23 2021, 6:27 AM
jmorse requested review of this revision.Jul 23 2021, 6:27 AM
Herald added a project: Restricted Project. · View Herald TranscriptJul 23 2021, 6:27 AM

Can we make a test?

llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
1663

Should it be isMetaInstruction() ?

Can we make a test?

Awkwardly, I'm not sure -- for some IR that looks like this, with function attribute sspstrong:

musttail call void %65(%foo *%call.i.i28.i.i.i, %bar* %60, i32 %63, i32 255), !dbg !12
ret void, !dbg !12

We usually get the following MIR after SelectionDAG runs, with an additional stack safety check put in before the tail call, to abort if the stack has overflowed,

  %151:gr64 = MOV64rm [Load of stack canary value]
  %152:gr64 = SUB64rm %151:gr64(tied-def 0), %stack.0.StackGuardSlot1, 1, $noreg, 0, $noreg, implicit-def $eflags
  JCC_1 %bb.78, 5, implicit $eflags
  JMP_1 %bb.40

bb.40.SP_return:
  %153:gr32 = MOV32ri 255
  $rdi = COPY %25:gr64, debug-location !12; foo.cpp:10
  $rsi = COPY %24:gr64, debug-location !12; foo.cpp:10
  $edx = COPY %26:gr32, debug-location !12; foo.cpp:10
  $ecx = COPY %153:gr32, debug-location !12; foo.cpp:10
  TCRETURNri64 %27:gr64_tc, 0, <regmask>, implicit $rsp, implicit $ssp, implicit $rdi, implicit $rsi, implicit $edx, implicit $ecx, debug-location !12; foo.cpp:10

Normally the modified function allows LLVM to identify that all the physreg are part of the tail-call-termination sequence. However if you put a DBG_INSTR_REF or DBG_PHI immediately in front of the TCRETURNri64, it breaks up the sequence of instructions in a way that the MachineVerifier complains about.

I'm not really sure how a debug instruction gets put between the TCRETURNri64 and the COPYs, there aren't any new values defined in that sequence, and those COPYs are glued to the return instruction by SelectionDAG. There are some comments in ScheduleDAGSDNodes.cpp about dropping loose DBG_VALUEs before the final terminator, I'll try a build with a breakpoint stuck in there, but I'm not hopeful.

llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
1663

The implications of this are unclear to me -- the "KILL" instruction for example manually edits register liveness information, and I'm not sure where in a termination sequence it would belong. IMO, best to keep the change so as small an amount of behaviours as possible, hence picking isDebugInstr.

(I wouldn't insist this is held up until a test is added - but I've got a strong preference for a test to be added. If it's hard to find a case, perhaps adding an assertion (faster than a breakpoint/running the whole thing under a debugger) isDebugValue || !isDebugInstr I think would be the interesting property to assert - and then hopefully running that over a codebase should yield something, then creduce/llvm-reduce checking for that assertion might reduce out to something useful?)

jmorse updated this revision to Diff 361613.Jul 26 2021, 4:02 AM

Various stage2reldeb builds of things didn't reproduce this, but I've managed to mangle a testcase (CodeGen/ARM/dbg-tcreturn.ll) to do what I want. It turns out the key interactions are:

  • IR operation transformed into a call,
  • Call optimised into a tail call,

Which is what smuggles a debug instruction to the end of the block without other guards catching it. After that, sspstrong is needed to trigger stack check insertion, which then mis-interprets the DBG_INSTR_REF.

Orlando accepted this revision.Jul 28 2021, 6:51 AM

LGTM with the new test which appears to be the only previously outstanding request.

llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
1663

SGTM. I just want to check that @djtodoro is happy with this too?

This revision is now accepted and ready to land.Jul 28 2021, 6:51 AM
djtodoro accepted this revision.Jul 28 2021, 7:08 AM
djtodoro added inline comments.
llvm/lib/CodeGen/SelectionDAG/SelectionDAGISel.cpp
1663

All good. I agree with Jeremy that other meta instructions shouldn't affect this piece of code.

This revision was landed with ongoing or failed builds.Jul 28 2021, 7:56 AM
This revision was automatically updated to reflect the committed changes.