This is an archive of the discontinued LLVM Phabricator instance.

[SCCP] do not clean up dead blocks that have their address taken
ClosedPublic

Authored by nickdesaulniers on Mar 15 2022, 3:00 PM.

Details

Summary

[SCCP] do not clean up dead blocks that have their address taken

Fixes a crash observed in IPSCCP.

Because the SCCPSolver has already internalized BlockAddresses as
Constants or ConstantExprs, we don't want to try to update their Values
in the ValueLatticeElement. Instead, continue to propagate these
BlockAddress Constants, continue converting BasicBlocks to unreachable,
but don't delete the "dead" BasicBlocks which happen to have their
address taken. Leave replacing the BlockAddresses to another pass.

Fixes: https://github.com/llvm/llvm-project/issues/54238
Fixes: https://github.com/llvm/llvm-project/issues/54251

Diff Detail

Event Timeline

Herald added a project: Restricted Project. · View Herald TranscriptMar 15 2022, 3:00 PM
nickdesaulniers requested review of this revision.Mar 15 2022, 3:00 PM
Herald added a project: Restricted Project. · View Herald TranscriptMar 15 2022, 3:00 PM
llvm/lib/Transforms/Scalar/SCCP.cpp
527–530

There's probably some code somewhere in llvm::SimplifyInstruction that already does this transform (as observed by SimplifyCFG in https://github.com/llvm/llvm-project/issues/54328#issuecomment-1067229660). maybe that can be reused here?

Since the behavior is surprising to users (https://github.com/llvm/llvm-project/issues/54328) maybe we could consolidate such transforms with a comment about why this is done, for historical context?

llvm/lib/Transforms/Utils/SCCPSolver.cpp
424–426 ↗(On Diff #415599)

This added bitcast is a bit curious, I'd like to clean it up, but I'm not sure yet how best to do so.

Basically, we know we want to replace i8* blockaddress(@fn, %bb) with i8* inttoptr (i32 1 to i8*), but the User of the BA and entry in ValueState is %1 = bitcast i8* blockaddress(@fn, %bb) to i64*.

Maybe I should be doing an operand replacement? As in take Old and replace its operand with New?

nickdesaulniers planned changes to this revision.Mar 15 2022, 3:12 PM
llvm/lib/Transforms/Utils/SCCPSolver.cpp
424–426 ↗(On Diff #415599)

Looks like User::replaceUsesOfWith can't be called with a Constant. Maybe I need to construct a new Instruction to replace the prior use, with the new operand?

llvm/lib/Transforms/Utils/SCCPSolver.cpp
424–426 ↗(On Diff #415599)

Looks like I need to be doing checks for ConstantExprs in the caller rather than dealing strictly with Constants.

  • handle ConstExpr's rather than just BitCasts
nickdesaulniers planned changes to this revision.Mar 16 2022, 11:08 AM
nickdesaulniers added inline comments.
llvm/lib/Transforms/Scalar/SCCP.cpp
533–542

While this removes the special case for bitcasts, it's still subtly not quite right; it only checks for one level of nesting of ConstExprs; there could be multiple "levels of nesting" for the aggregated Constant stored in the lattice.

i.e. the BlockAddress of interest might have the following User: i64* bitcast (i8* blockaddress(@main, %redirected) to i64*); but I worry if the lattice had something like i8* bitcast (i64* bitcast (i8* blockaddress(@main, %redirected) to i64*) to i8*); then we'd be trying to replace the former and not finding the latter. Unless the latter is also considered a User of the BlockAddress?

I can test that, but I'm going to see first whether we can query the solver whether it has reach-ability info ready BEFORE we internalize new ConstantExpr's containing BlockAddress Constants into the lattice. Then we could avoid this find+replace dance and simply do less work.

llvm/lib/Transforms/Scalar/SCCP.cpp
533–542

but I'm going to see first whether we can query the solver whether it has reach-ability info ready BEFORE we internalize new ConstantExpr's containing BlockAddress Constants into the lattice. Then we could avoid this find+replace dance and simply do less work.

Yeah, the SCCPVisitor marks blocks executable during a traversal, while eagerly adding values to the lattice; so block executability is not precise until we've completed the traversal. At that point, it's too late as we've already merged values into the lattice.

I think my preferred solution here would be to delay deleting dead blocks further, until the lattice stops mattering.

Any solution that involves trying to explicitly fix up the lattice, like the current patch, is going to cause problems for correctness and perf, I think.

For your other proposed solution, I don't think you can tell whether a block is reachable at the point where the blockaddress shows up in the lattice. You could blacklist all blockaddresses from the lattice, I guess, for a minor loss of optimization power.

You could also possibly use a TrackingVH in ValueLatticeElement; not sure what consequences that would have.

I think my preferred solution here would be to delay deleting dead blocks further, until the lattice stops mattering.

diff --git a/llvm/lib/Transforms/Scalar/SCCP.cpp b/llvm/lib/Transforms/Scalar/SCCP.cpp
index 794d4c5b5bc6..e9e3e90df875 100644
--- a/llvm/lib/Transforms/Scalar/SCCP.cpp
+++ b/llvm/lib/Transforms/Scalar/SCCP.cpp
@@ -505,7 +505,7 @@ bool llvm::runIPSCCP(
 
         MadeChanges = true;
 
-        if (&BB != &F.front())
+        if (&BB != &F.front() && !BB.hasAddressTaken())
           BlocksToErase.push_back(&BB);
         continue;
       }

?

efriedma added inline comments.Mar 16 2022, 12:10 PM
llvm/lib/Transforms/Scalar/SCCP.cpp
533–534

Something more like this, I think? Assuming this doesn't have any side-effects I'm not thinking of.

llvm/lib/Transforms/Scalar/SCCP.cpp
533–534

yeah, that's the simplest fix.

I'm going to pre-commit updating llvm/test/Transforms/SCCP/dangling-block-address.ll's RUN line to use NPM, and run it through update_test_checks.py, just so we can see what changes.

  • rebase on pre-committed test changes. Use @efriedma's sugguestion.
nickdesaulniers retitled this revision from [SCCP] Update ValueLatticeElement blockaddresses when removing unreachable BasicBlocks to [SCCP] do not clean up dead blocks that have their address taken.Mar 16 2022, 2:06 PM
nickdesaulniers edited the summary of this revision. (Show Details)
nickdesaulniers marked an inline comment as done.
nikic accepted this revision.Mar 17 2022, 1:32 AM

LGTM. We could still delay these blocks later (after all functions have been handled), but I don't think it would be worthwhile.

This revision is now accepted and ready to land.Mar 17 2022, 1:32 AM
This revision was landed with ongoing or failed builds.Mar 18 2022, 11:02 AM
This revision was automatically updated to reflect the committed changes.