This is an archive of the discontinued LLVM Phabricator instance.

[InstCombine] Aggressively fold compares that can be discovered to be constant
AbandonedPublic

Authored by jmolloy on Dec 4 2015, 8:22 AM.

Details

Summary

There are situations where we may not be able to determine that a value is a particular constant, but we may be able to determine that it must be one of a set of constants. A good example is when trawling through selects or PHIs:

%1 = select i1 %a, i32 42, i32 64
%2 = icmp sgt i32 %1, i32 78

We know %1 must either be 42 or 64. We should be able to use that knowledge to fold %2 to false. In fact, we can do this already for the above simple case due to peepholes in InstCombineCompares. However the following case does not get caught:

%1 = select i1 %a, i32 42, i32 64
%2 = add i32 %1, 1
%3 = icmp sgt i32 %2, i32 78

Because the peepholes in InstCombine can't see through the add.

This patch introduces a general GetUnderlyingValues() function, sort of a corrollary to GetUnderlyingObjects(). It will take a value and attempt to determine a set of other values it could possibly be. In this case GetUnderlyingValues(%2) would return {43, 64}.

We then use this in InstCombineCompares - if all the results returned by GetUnderlyingValues are constant, and the constant folder determines the result of the compare is the same in all cases, constant fold the compare.

We do this in InstCombine rather than InstSimplify primarily because GetUnderlyingValues (like GetUnderlyingObjects) can call into InstSimplify itself and therefore cause unchecked recursion. Calling this only from InstCombine is very nearly as powerful and uses less compile time too (no measured compile time increase).

Diff Detail

Event Timeline

jmolloy updated this revision to Diff 41882.Dec 4 2015, 8:22 AM
jmolloy retitled this revision from to [InstCombine] Aggressively fold compares that can be discovered to be constant.
jmolloy updated this object.
jmolloy added reviewers: majnemer, hfinkel, mcrosier.
jmolloy set the repository for this revision to rL LLVM.
jmolloy added a subscriber: llvm-commits.
majnemer added inline comments.Dec 4 2015, 8:59 AM
lib/Analysis/ValueTracking.cpp
3049

auto *

3055

auto *

3062

auto *

3065

auto *

3068–3069

Does isa<ConstantInt> fulfill UnaryPredicate? If so, you don't need the lambda.

3077–3082

It doesn't look like NewVs will be used if Fail is true. Why not break out of the loop?

3090

auto *

lib/Transforms/InstCombine/InstCombineCompares.cpp
2764

auto *

jmolloy updated this revision to Diff 41889.Dec 4 2015, 9:16 AM
jmolloy removed rL LLVM as the repository for this revision.

Hi David,

Thanks for the quick review! All the auto's done apart from one due to a strange type deduction error:

error: variable 'IncValue' with type 'auto *' has incompatible initializer of type 'llvm::Use'
    for (auto *IncValue : PN->incoming_values())

Unfortunately isa<> can't be deduced as a UnaryPredicate either, which is a pity because I thought that would look really neat.

Cheers,

James

bkramer added a subscriber: bkramer.Dec 4 2015, 9:26 AM

Unfortunately isa<> can't be deduced as a UnaryPredicate either, which is a pity because I thought that would look really neat.

You can't use isa<Foo>, but you can use isa<Foo, const Value *>. It's not as pretty though.

reames added a subscriber: reames.Dec 4 2015, 12:36 PM

At a high level, I'm a bit unsure about the formulation chosen here. I'm worried that seeking to find a set of constant values through a potentially large value graph might be expensive.

A couple of specific questions:

  • The examples you've listed should be entirely handled by LazyValueInfo and either JumpThreading or CVP. Do you have a strong reason InstCombine needs to perform the same optimization?
  • Have you considered phrasing this as pushing the query (i.e. icmp) back along the inputs? Doing this might a) let you terminate earlier or b) unswitch the comparison if the select is only used by the compare.
hfinkel edited edge metadata.Dec 10 2015, 7:14 AM

Can you provide a better example, because InstCombine seems to get the one you mentioned already (I just tried with a build using r254106):

$ cat /tmp/f.cpp 
bool check(bool i) {
  int f = i ? 42 : 64;
  f += 1;
  return f > 78;
}

which eventually gets down to:

define zeroext i1 @_Z5checkb(i1 zeroext %i) #0 {
entry:
  %frombool = zext i1 %i to i8
  %tobool = trunc i8 %frombool to i1
  %cond = select i1 %tobool, i32 42, i32 64
  %add = add nsw i32 %cond, 1
  %cmp = icmp sgt i32 %add, 78
  ret i1 %cmp
}

and then (after instcombine):

; Function Attrs: nounwind
define zeroext i1 @_Z5checkb(i1 zeroext %i) #0 {
entry:
  ret i1 false
}

Beyond this, if we need further functionality in instcombine, did you consider using LVI directly?

Hi Philip,

Would you feel comfortable reviewing the rest of the code? I'm aware that David is rather swamped with reviews and am trying to find alternative reviewers.

Cheers,

James

jmolloy abandoned this revision.Dec 10 2015, 8:53 AM