This is an archive of the discontinued LLVM Phabricator instance.

[ValueTracking] Improve isImpliedCondition for conditions with matching operands.
ClosedPublic

Authored by mcrosier on Apr 8 2016, 12:34 PM.

Details

Summary

This patch improves SimplifyCFG to catch things like:

if (a < b) {
  if (b < a) <- known to be false
    unreachable;
}

and

if (a > b) {
  if (b < a) { <- always true; remove control flow
    always reachable;
  }
}

The later case is generally captured when the compares are canonicalized, but in my testing I found that there are cases in the wild where compares aren't always in canonical form. I do plan on addressing the FIXME in jump threading, but I wanted to get this patch approved first.

Please take a look,
Chad

Diff Detail

Event Timeline

mcrosier updated this revision to Diff 53063.Apr 8 2016, 12:34 PM
mcrosier retitled this revision from to [ValueTracking] Improve isImpliedCondition for conditions with matching but swapped operands..
mcrosier updated this object.
mcrosier added reviewers: sanjoy, reames.
mcrosier set the repository for this revision to rL LLVM.
mcrosier updated this object.
mcrosier added a subscriber: llvm-commits.
mcrosier added inline comments.Apr 8 2016, 12:38 PM
test/Transforms/SimplifyCFG/implied-cond-swapped.ll
40 ↗(On Diff #53063)

This should be (b < a). I've already updated my local version. The IR itself is correct.

This is an optimization identified by Souper.

sanjoy requested changes to this revision.Apr 8 2016, 1:01 PM
sanjoy edited edge metadata.
sanjoy added inline comments.
include/llvm/Analysis/ValueTracking.h
440

I'd use something a little more descriptive -- maybe ImpliedTrue?

The Right(TM) fix here is to change isImpliedCondition to return Optional<bool>, but I don't know how much work that will be.

lib/Analysis/ValueTracking.cpp
3818

Minor: no braces need here.

lib/Transforms/Utils/SimplifyCFG.cpp
2733–2735

Huh? Why static ?

sanjoy added inline comments.Apr 8 2016, 1:01 PM
lib/Analysis/ValueTracking.cpp
3815

I'm not sure this is correct for "or equal" predicates -- A >= B does not imply !(B >= A) since if A == B then both of these are true.

This revision now requires changes to proceed.Apr 8 2016, 1:01 PM
mcrosier updated this revision to Diff 53086.Apr 8 2016, 2:06 PM
mcrosier edited edge metadata.
mcrosier removed rL LLVM as the repository for this revision.

Address Sanjoy's comments.

mcrosier marked 3 inline comments as done.Apr 8 2016, 2:08 PM

Thanks for the speedy feedback, Sanjoy!

include/llvm/Analysis/ValueTracking.h
440

I've updated everything to ImpliedTrue. Mind if I defer the second suggestion? :)

lib/Analysis/ValueTracking.cpp
3815

You're absolutely correct. Thanks!!

lib/Transforms/Utils/SimplifyCFG.cpp
2733–2736

cut-pasteo. Removed.

sanjoy requested changes to this revision.Apr 8 2016, 4:04 PM
sanjoy edited edge metadata.
sanjoy added inline comments.
include/llvm/Analysis/ValueTracking.h
440

SGTM.

But I'd change the doxygen comment slightly -- 'depending on the value of ImpliedTrue' -> 'depending on what is returned in ImpliedTrue', to make it obvious that ImpliedTrue is a return parameter.

lib/Analysis/InstructionSimplify.cpp
2134

I didn't notice these last time, but I'm not sure if this usage is correct (or will remain correct, even if they're correct in practice after this patch :)): isImpliedCondition will return true with ImpliedTrue set to false if RHS -> NOT(LHS). But in that case you're simplifying RHS -> LHS to false, meaning you're asserting NOT(RHS -> LHS). NOT(RHS -> LHS) is not the same as RHS -> NOT(LHS) if RHS is false.

This revision now requires changes to proceed.Apr 8 2016, 4:04 PM
mcrosier updated this revision to Diff 53136.Apr 9 2016, 9:04 AM
mcrosier edited edge metadata.
mcrosier marked an inline comment as done.

Address Sanjoy's comments:
-Update comment to make it more explicit that ImpliedTrue is a return value.
-Correct the logic in InstSimplify.

mcrosier marked 2 inline comments as done.Apr 9 2016, 9:05 AM
mcrosier updated this revision to Diff 53559.Apr 13 2016, 8:16 AM
mcrosier retitled this revision from [ValueTracking] Improve isImpliedCondition for conditions with matching but swapped operands. to [ValueTracking] Improve isImpliedCondition for conditions with matching operands..
mcrosier edited edge metadata.

-Make this patch much more general by not insisting the operands be swapped.
-Address the FIXME by handling the remaining comparisons (e.g., A == B implies A >B, A <B, and A != B are false, etc.).

Passes all correctness test for the test-suite, SPEC200X, EEMBC and a clang bootstrap.

I believe this is in pretty good shape.

Chad

sanjoy requested changes to this revision.Apr 13 2016, 11:05 PM
sanjoy edited edge metadata.
sanjoy added inline comments.
lib/Analysis/ValueTracking.cpp
3830

This scares me. Can you use a different variable name for APred and BPred here, like UnsignedAPred?

3857

Won't this return true for A ugt B => NOT(A sle B) (since you unconditionally coerced both to the unsigned variants) (consider A = -1, B = 0)?

This revision now requires changes to proceed.Apr 13 2016, 11:05 PM

Thanks for the feedback, Sanjoy. Let me know if my comments make sense. I'll upload a new diff shortly.

lib/Analysis/ValueTracking.cpp
3830

Sure, no problem. I don't want to scare anyone. :)

3857

See my comment below about checking to make sure the predicates share the same sign, if they're signed. I believe such a check would avoid this situation.

3924

At minimum, I'm thinking we need a check here to make sure both compares have the same sign (or at least one compare is an equality check, which has no sign).

We don't think we want 'icmp ugt a, b' to imply 'icmp sgt b, a' is false, for example.

I'll add tests cases.

mcrosier updated this revision to Diff 53753.Apr 14 2016, 10:38 AM
mcrosier edited edge metadata.

Address Sanjoy's feedback by:
-Renaming predicates that are unconditionally converted to unsigned predicate.
-Add a check to make sure we're never comparing predicates of different signs (with associated test case).

mcrosier marked 5 inline comments as done.Apr 14 2016, 10:39 AM
gberry edited edge metadata.Apr 14 2016, 11:03 AM

Added a couple of comments, still looking over other bits of the change...

lib/Analysis/ValueTracking.cpp
3907–3910

The value returned in ImpliedTrue is undefined (and may be checked by the caller) if this path is taken.

lib/Transforms/Scalar/JumpThreading.cpp
938

This seems like an easy thing to go ahead and fix. Just factor out the 0 and 1 successor indices above and swap them if ImpliedTrue is false, right?

mcrosier updated this revision to Diff 53770.Apr 14 2016, 12:31 PM
mcrosier edited edge metadata.

Address Geoff's comments:
-Fix undef of IsImpliedCond when LHS == RHS
-Enable changes in jump threading

mcrosier marked 2 inline comments as done.Apr 14 2016, 12:32 PM
gberry added inline comments.Apr 14 2016, 12:34 PM
lib/Analysis/ValueTracking.cpp
3803

Perhaps this function could be simplified by canonicalizing to the IsMatchingOps case at the beginning:

if (IsSwappedOps) {
  std::swap(BLHS, BRHS);
  BPred = ICmpInst::getSwappedPredicate(BPred);
}

Then you don't need to consider the IsSwappedOps cases below.

3927

This check seems like it should be pushed down into isImpliedCondMatchingOperands since it doesn't necessarily apply to isImpliedCondOperands? I feel like maybe I'm missing something here.

3935

I don't follow why this check needs to be added when isImpliedCondOperands has not been changed.

mcrosier added inline comments.Apr 15 2016, 6:25 AM
lib/Analysis/ValueTracking.cpp
3803

Sure. I'll need to add the cases to the switch at the bottom, but I agree it will likely make the code less confusing.

3927

Based on experiments with InstCombine, I believe this condition must always be true to infer anything about the two conditions. My expectation is that other improvements will come along (e.g., D19073) that also require this check. Therefore, I went ahead an put the check here, rather than in isImpliedCondMatchingOperands(), to make sure I or others don't miss this important requirement in the future.

I can sink it if you like, but I expect my follow on patches will just pull this logic back out.

3935

if you're referring to

if (APred == BPred) {

that check is not new. :)

mcrosier updated this revision to Diff 53879.Apr 15 2016, 7:08 AM

Address Geoff's comments:
-Canonicalize operands so they match to simplify logic.

Other minor refactoring.

mcrosier marked 2 inline comments as done.Apr 15 2016, 7:09 AM
gberry added inline comments.Apr 15 2016, 8:13 AM
lib/Analysis/ValueTracking.cpp
3935

Ah, okay, I misread the diff before.

bmakam added a subscriber: bmakam.Apr 15 2016, 9:52 AM
bmakam added inline comments.
test/Transforms/SimplifyCFG/implied-cond-matching.ll
116

Won't this be always reachable?

bmakam added inline comments.Apr 15 2016, 9:54 AM
test/Transforms/SimplifyCFG/implied-cond-matching.ll
116

Please ignore this comment, this should be always unreachable

bmakam added a comment.EditedApr 15 2016, 10:40 AM

Perhaps instcombine is a better place to catch this? I verified PR23333 could be caught by this approach. While improving D18841 I could handle PR23333 and almost all the cases mentioned in here except for test_gt, test_ltand test_eq. I can easily handle these cases but I have currently limited D18841 to the immediate dominator of depth 1 in favor of walking the entire dominator tree for compile-time tradeoff. I am not sure which approach is better. Perhaps you could improve this to also handle PR23333 and other tests in D18841 or perhaps both solutions could co-exist.

Perhaps instcombine is a better place to catch this? I verified PR23333 could be catched by this approach. While improving D18841 I could handle PR23333 and almost all the cases mentioned in here except for test_gt, test_ltand test_eq. I can easily handle these cases but I have currently limited D18841 to the immediate dominator of depth 1 in favor of walking the entire dominator tree for compile-time tradeoff. I am not sure which approach is better. Perhaps you could improve this to also handle PR23333 and other tests in D18841 or perhaps both solutions could co-exist.

I think the right plan of attack is to implement this logic here and then have other passes use the isImpliedCondition() API as is already done in SimplifyCFG, Jump Threading, and InstSimplify.

The logic added in this patch can prove that the first condition in PR23333 implies the second. It will also catch the test cases in D18841 if isImpliedCondition() were used in InstCombine as you suggest.

sanjoy requested changes to this revision.Apr 18 2016, 2:07 PM
sanjoy edited edge metadata.
sanjoy added inline comments.
lib/Analysis/ValueTracking.cpp
3801

I'd change the comment to be in terms of the formal parameters (i.e. APred, ARHS etc.) to make things more obvious.

3832

Nit: I'd use auto here.

3834

This does not need to happen in this change, but it really looks like this logic is general enough to live in CmpInst along with things like isTrueWhenEqual as CmpInst::isImpliedPredicate.

3838

Can't you use CmpInst::isFalseWhenEqual here?

3876

Some of this boilerplate can be removed by checking if getInversePredicate(APred) == BPred outside the switch.

3907–3911

This is why I'd have liked an Optional<bool> return value. :) Avoids mistakes like these.

3926

I wouldn't do the check here, since it isn't obvious how this check relates to isImpliedCondMatchingOperands or even why the precondition is needed. Instead, I'll change the assert in isImpliedCondMatchingOperands to return false if the predicates are not comparable.

This revision now requires changes to proceed.Apr 18 2016, 2:07 PM
mcrosier updated this revision to Diff 54138.Apr 18 2016, 4:01 PM
mcrosier edited edge metadata.
mcrosier marked 2 inline comments as done.

Address Sanjoy's comments.

mcrosier marked 5 inline comments as done.Apr 18 2016, 4:03 PM
mcrosier added inline comments.
lib/Analysis/ValueTracking.cpp
3834

I'll need to investigate. Thanks for the suggestion, Sanjoy.

3907–3911

:/ I'll address this in a follow up commit.

sanjoy accepted this revision.Apr 18 2016, 8:57 PM
sanjoy edited edge metadata.

lgtm with two nits inline

Other than that I'll urge you to try to refactor the tricky predicate logic in isImpliedCondMatchingOperands into CmpInst soon.

lib/Analysis/ValueTracking.cpp
3829

Nit: I'd just inline the body here. PredicatesComparable isn't terribly descriptive or general, and the function is a two liner anyway.

3842

Please verify this, but I don't think you need to enumerate the predicates here, and you don't even need to guard this under PredicatesComparable. You can just straightforwardly do;

if (CmpInst::getInversePredicate(UnsignedAPred) == UnsignedBPred) {
  ImpliedTrue = false;
  return true;
}
This revision is now accepted and ready to land.Apr 18 2016, 8:57 PM
mcrosier closed this revision.Apr 19 2016, 10:40 AM
mcrosier marked 2 inline comments as done.

Thanks, Sanjoy. I'll follow up on your suggestions shortly.

Committed r266767.

I'll urge you to try to refactor the tricky predicate logic in isImpliedCondMatchingOperands into CmpInst soon.

Please let me know if D19330 is along the lines of what you were suggesting, Sanjoy.