This is an archive of the discontinued LLVM Phabricator instance.

Avoid infinite loops in branch folding
ClosedPublic

Authored by andrew.w.kaylor on Dec 8 2016, 11:02 AM.

Details

Summary

This reintroduces a fix for a problem where the branch folding optimization can go into an infinite loop shuffling blocks at the end of a function. This problem was originally fixed in D14996 but that change was reverted as seen in D22839 because it was causing compile time to blow up in some circumstances. At the time this was reverted I believed that the original problem could no longer be reproduced, but I have since seen it with a particular set of optimization options with the spec2006/483 benchmark.

The test case I am adding here is a version of the 483 code that was reduced using bugpoint. I am not clear as to exactly why this particular case is triggering the problem, but I wasn't able to reduce it any further. I suspect that the state it is in now just happens to trigger different behavior prior to branch folding when optimizing for size.

In any event, the important thing is that the potential for the problem still exists. The root cause is that when the branch folding code is hit there is a block near the end of the function with a function call followed by a branch to a block that is higher in the function, then three or more exception handling blocks which are possible successors by way of exceptions in the called function. In this case, the code prior to this patch would rotate the exception blocks to the end of the function in infinite succession.

The compilation time problem was caused by the previous attempt to find a non-exception handling block beyond the block to be moved. In a function containing a large number of exception handling blocks this led to quadratic or worse behavior. The new fix simply gives up if the block following the block to be moved is an exception handling block.

Diff Detail

Repository
rL LLVM

Event Timeline

andrew.w.kaylor retitled this revision from to Avoid infinite loops in branch folding.
andrew.w.kaylor updated this object.
andrew.w.kaylor added reviewers: rnk, MatzeB.
andrew.w.kaylor set the repository for this revision to rL LLVM.
andrew.w.kaylor added a subscriber: llvm-commits.
rnk added inline comments.Dec 8 2016, 4:07 PM
lib/CodeGen/BranchFolding.cpp
1637 ↗(On Diff #80784)

Let's check isEHPad before analyzeBranch. It's cheaper, and it makes no sense to try to arrange a fallthrough to an EH pad. The analogy with landingpads would be this situation:

  invoke ... to %unreachable unwind %lpad
next: ; cannot arrange fallthrough for some reason
  ret void
lpad:
  landingpad ...

Without your change, it looks like we'll try to get lpad to follow the invoke, which isn't useful.

test/CodeGen/X86/branchfolding-catchpads.ll
98 ↗(On Diff #80784)

Can we manually make this simpler?

lib/CodeGen/BranchFolding.cpp
1637 ↗(On Diff #80784)

OK, that definitely makes sense.

test/CodeGen/X86/branchfolding-catchpads.ll
98 ↗(On Diff #80784)

You'd really think so, wouldn't you? I tried removing most of these pieces and even taking out one of the arguments to the g() call or removing one of the switch branches to unreachable blocks avoids the infinite loop. I strongly suspect that this combination of things is just pushing it over some threshold for a size optimization, but I have to admit that I don't completely understand it. I'm running the test without optimizations (llc does default to OptNone, right?), but even just removing the optsize function attribute causes this test case not to hang. I'll try a run that dumps before every pass to see if I can figure out what's really going on.

Honestly, I'm not sure how useful this is as a regression test. It reproduces the failure right now, but I have no reason to think that it will still reproduce the failure a month from now. What it does accomplish, however, is documenting the kind of complexity that can trigger this bug. When I reverted the previous fix, I did so because I had convinced myself that this failure wasn't possible anymore after trying a few variations on the other two test cases in this file. I'd like to hope that anyone looking at this test case in the future would just think "OK, let's just assume that it's still possible." Maybe that's better done by adding some foreboding comments in the code.

I moved the isEHPad() check, as suggested.

I was able to reduce the test case a little more. The arguments to g() were only necessary to prevent a tail call optimization. Simply removing 'tail' from the call instruction dealt with that. I was also able to remove a few of the switch statement entries and simplify the control flow slightly. The remaining complexity is necessary to prevent intermediate passes (I think mostly the instruction selector) from simplifying a few remaining branches and reordering blocks such that the conditions for the failure are no longer met.

I updated the comments to better explain the failure condition a bit more.

rnk accepted this revision.Dec 12 2016, 8:28 AM
rnk edited edge metadata.

lgtm

test/CodeGen/X86/branchfolding-catchpads.ll
98 ↗(On Diff #80784)

In theory we have MIR so that we can dump the MI right before the problematic branch folding pass.

It might be possible to do an MIR test that shows we don't attempt to fall through to EH pads, instead of trying to test that we don't infinite loop.

Anyway, these are suggestions, I'm happy with the test as is.

This revision is now accepted and ready to land.Dec 12 2016, 8:28 AM
This revision was automatically updated to reflect the committed changes.