This is an archive of the discontinued LLVM Phabricator instance.

[JumpThreading] Fix exponential time algorithm computing known values.
ClosedPublic

Authored by efriedma on Nov 7 2018, 4:24 PM.

Details

Summary

ComputeValueKnownInPredecessors has a "visited" set to prevent infinite loops, since a value can be visited more than once. However, the implementation didn't prevent the algorithm from taking exponential time. Instead of removing elements from the RecursionSet one at a time, we should keep around the whole set until ComputeValueKnownInPredecessors finishes, then discard it.

The testcase is synthetic because I was having trouble effectively reducing the original. But it's basically the same idea.

Instead of failing, we could theoretically cache the result instead. But I don't think it would help substantially in practice.

Diff Detail

Repository
rL LLVM

Event Timeline

efriedma created this revision.Nov 7 2018, 4:24 PM
mkazantsev accepted this revision.Nov 7 2018, 10:30 PM

LGTM.

It seems that the initial design of this algorithm wasn't intended to make 2 recursive calls from 1 place. The interesting bit is that we only can have an exponential explosion here:

// Handle some boolean conditions.
if (I->getType()->getPrimitiveSizeInBits() == 1) {
  assert(Preference == WantInteger && "One-bit non-integer type?");
  // X | true -> true
  // X & false -> false
  if (I->getOpcode() == Instruction::Or ||
      I->getOpcode() == Instruction::And) {
    PredValueInfoTy LHSVals, RHSVals;

    ComputeValueKnownInPredecessors(I->getOperand(0), BB, LHSVals,
                                    WantInteger, CxtI);
    ComputeValueKnownInPredecessors(I->getOperand(1), BB, RHSVals,
                                    WantInteger, CxtI);

In all other places, we seem to make just 1 recursive call and therefore no growth in width. In your particular case the obvious fix would be to just check that operand(0) == operand(1), but I guess that it's easy enough to construct a test when they won't match with same effect, e.g.

%x1 = or i1 %x0, %x0
%x2 = or i1 %x1, %x1
%x3 = or i1 %x1, %x2
%x4 = or i1 %x2, %x3

Do you mind adding such test as well? Just to make sure that no one rules it out with a trivial partial fix.

This revision is now accepted and ready to land.Nov 7 2018, 10:30 PM
brzycki accepted this revision.Nov 8 2018, 7:41 AM

+1 for me as well. JumpThreading, as it stands today, is quite haphazard in how it attacks a function and anything that helps reduce time spent recomputing known values is desirable.

I'm curious if you have compile time deltas from test-suite CTMark to see the results.

This revision was automatically updated to reflect the committed changes.