This is an archive of the discontinued LLVM Phabricator instance.

Add a pass to garbage-collect empty basic blocks after code generation.
ClosedPublic

Authored by rahmanl on Aug 4 2021, 10:04 PM.

Details

Summary

Propeller and pseudo-probes map profiles back to Machine IR via basic block addresses that are stored in metadata sections.
Empty basic blocks (basic blocks without real code) obfuscate the profile mapping because their addresses collide with their next basic blocks.
For instance, the fallthrough block of an empty block should always be adjacent to it. Otherwise, a completely unnecessary jump would be added.
This patch adds a MachineFunction pass named GCEmptyBasicBlocks which attempts to garbage-collect the empty blocks before the BasicBlockSections and pass.
This pass removes each empty basic block after redirecting its incoming edges to its fall-through block.
The garbage-collection is not complete. We keep the empty block in 4 cases:

  1. The empty block is an exception handling pad.
  2. The empty block has its address taken.
  3. The empty block is the last block of the function and it has predecessors.
  4. The empty block is the only block of the function.

The first three cases are extremely rare in normal code (no cases for the clang binary). Removing the blocks under the first two cases requires modifying exception handling structures and operands of non-terminator instructions -- which is doable but not worth the additional complexity in the pass.

Diff Detail

Event Timeline

rahmanl created this revision.Aug 4 2021, 10:04 PM
rahmanl updated this revision to Diff 364355.Aug 4 2021, 10:50 PM

Add comments.

rahmanl updated this revision to Diff 364356.Aug 4 2021, 10:51 PM

Add comments.

rahmanl updated this revision to Diff 364360.Aug 4 2021, 10:57 PM

Revert unrelated changes.

rahmanl removed a project: Restricted Project.
rahmanl removed subscribers: jholewinski, qcolombet, nemanjai and 62 others.
rahmanl updated this revision to Diff 548122.Aug 8 2023, 2:29 AM

Update and rebase the patch.

Herald added a project: Restricted Project. · View Herald TranscriptAug 8 2023, 2:29 AM
rahmanl updated this revision to Diff 548287.Aug 8 2023, 11:02 AM

Add one more test.

rahmanl published this revision for review.Aug 8 2023, 11:11 AM
rahmanl retitled this revision from Garbage-collect empty basic blocks for Propeller. to Garbage-collect empty basic blocks after code generation..
rahmanl edited the summary of this revision. (Show Details)
rahmanl added a reviewer: mtrofin.
Herald added a project: Restricted Project. · View Herald TranscriptAug 8 2023, 11:11 AM
rahmanl updated this revision to Diff 548294.Aug 8 2023, 11:33 AM

Remove unwanted change.

This revision is now accepted and ready to land.Aug 8 2023, 11:38 AM
rahmanl updated this revision to Diff 549090.Aug 10 2023, 10:10 AM

Omit empty basic blocks which have their address taken, and EH pad blocks.

rahmanl edited the summary of this revision. (Show Details)Aug 10 2023, 10:13 AM
rahmanl edited the summary of this revision. (Show Details)
rahmanl edited the summary of this revision. (Show Details)
rahmanl retitled this revision from Garbage-collect empty basic blocks after code generation. to Add a pass to garbage-collect empty basic blocks after code generation..Aug 10 2023, 12:27 PM
rahmanl edited the summary of this revision. (Show Details)
rahmanl edited reviewers, added: hoy, wenlei; removed: mtrofin, JestrTulip.
rahmanl added subscribers: mtrofin, JestrTulip.
This revision now requires review to proceed.Aug 10 2023, 12:27 PM
rahmanl updated this revision to Diff 549138.Aug 10 2023, 12:28 PM
rahmanl edited the summary of this revision. (Show Details)

clang-format.

tmsriram added inline comments.Aug 15 2023, 3:23 PM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

If the block has no real instructions but debug instructions, is it correct to nuke it?

59

If it is the last block it must have predecessors or you couldn't get to it, so !Preds.empty() is redundant.

71

Should we add the non-real instructions in this block to the beginning of the next block too?

wenlei added inline comments.Aug 15 2023, 4:28 PM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

For debuggbility, probably not. Relatedly the current implementation also keep the block if it contains pseudo-probe, which is what we want.

rahmanl added inline comments.
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

I am guessing debug-info instructions in a basic block with zero binary size is never used. Any ideas @dblaikie ?

@wenlei My initial idea was to GC all empty blocks before inserting pseudo probes. Now I realize pseudo probes may be inserted anytime. But I still think we can remove empty blocks with pseudo probes at this point of CodeGen. WDYT?

59

I found one case of a function with a single empty basic block which prompted me to add this check.

71

I am not sure we can. For example, a register kill instruction cannot be safely moved into the new block, if that block has other predecessors.

rahmanl added inline comments.Aug 15 2023, 7:17 PM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
59

To be more precise, if I do remove the single empty basic block in that function, the compilation later breaks (during CFI-insertion) because the function becomes empty. I will take a look to see if I can mitigate the breakage.

wenlei added inline comments.Aug 15 2023, 8:59 PM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

pseudo-probes are inserted very early - similar to where IRPGO probes are inserted. If a block has pseudo-probe in it, we should not remove that block, because the purpose of pseudo-probe is to preserve control flow (branches) so profile can be mapped to unoptimized IR more accurately.

rahmanl added inline comments.Aug 15 2023, 9:34 PM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

Thanks for the explanation. I got a bit confused looking at where PseudoProbeInserter is added in TargetPassConfig.cpp, which is only for callsites.

dblaikie added inline comments.
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

@jmorse for debug location intrinsic functions - but my guess would be deleting an unreachable block would be fine. I'd guess SimplifyCFG already does this? (you could try it -and if it does just delete the block, despite the presence of debug location intrinsics, it's probably a good sign you can do that too)

rahmanl updated this revision to Diff 551194.Aug 17 2023, 11:04 AM

Remove redundant check, refactor, rebase.

rahmanl added inline comments.Aug 17 2023, 11:07 AM
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
54

Here, we are deleting basic blocks with no real instructions (even if they are reachable). These basic blocks manifest in the binary as blocks which have the same begin address as their next block. So debuggers can use the debug-info from their next block instead.

59

Turns out my check if (MF.size() < 2) guards against this case already. Applied your suggestion and then refactored a bit.

rahmanl updated this revision to Diff 551196.Aug 17 2023, 11:10 AM

Remove unused variable.

tmsriram accepted this revision.Aug 22 2023, 11:49 AM

I am approving this change as it looks like all concerns have resolutions. Further, this is not the default.

This revision is now accepted and ready to land.Aug 22 2023, 11:49 AM
rahmanl updated this revision to Diff 552517.Aug 22 2023, 3:25 PM
  • Add check for stats.
  • Change the option name to -gc-empty-basic-blocks.
  • Rebase
This revision was landed with ongoing or failed builds.Aug 22 2023, 3:42 PM
This revision was automatically updated to reflect the committed changes.
lkail added a subscriber: lkail.Aug 23 2023, 1:48 AM
lkail added inline comments.
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
1
rahmanl marked an inline comment as done.Aug 23 2023, 10:46 AM
rahmanl added inline comments.
llvm/lib/CodeGen/GCEmptyBasicBlocks.cpp
1

Does this work with -mllvm -trap-unreachable?

rahmanl marked an inline comment as done.Aug 28 2023, 3:11 PM

Does this work with -mllvm -trap-unreachable?

It should. What are these trap instructions lowered to in MIR?