-
Notifications
You must be signed in to change notification settings - Fork 15.4k
ValueTracking: teach implied-cond about samesign #116614
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
isImpliedCondition benefits little from the icmp samesign feature: the only routine that can use this information is isImpliedCondICmps when it is matching against signed predicates, since icmp samesign is already canonicalized with an unsigned predicate. Add this support.
|
@llvm/pr-subscribers-llvm-analysis Author: Ramkumar Ramachandra (artagnon) ChangesisImpliedCondition benefits little from the icmp samesign feature: the only routine that can use this information is isImpliedCondICmps when it is matching against signed predicates, since icmp samesign is already canonicalized with an unsigned predicate. Add this support. Full diff: https://github.com/llvm/llvm-project/pull/116614.diff 2 Files Affected:
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index c48068afc04816..a3285f943bf880 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9268,6 +9268,7 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
// case, invert the predicate to make it so.
CmpInst::Predicate LPred =
LHSIsTrue ? LHS->getPredicate() : LHS->getInversePredicate();
+ bool LHasSameSign = LHS->hasSameSign();
// We can have non-canonical operands, so try to normalize any common operand
// to L0/R0.
@@ -9321,7 +9322,8 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
// must be positive if X >= Y and no overflow".
// Take SGT as an example: L0:x > L1:y and C >= 0
// ==> R0:(x -nsw y) < R1:(-C) is false
- if ((LPred == ICmpInst::ICMP_SGT || LPred == ICmpInst::ICMP_SGE) &&
+ if ((ICmpInst::isSigned(LPred) || LHasSameSign) &&
+ (ICmpInst::isGE(LPred) || ICmpInst::isGT(LPred)) &&
match(R0, m_NSWSub(m_Specific(L0), m_Specific(L1)))) {
if (match(R1, m_NonPositive()) &&
isImpliedCondMatchingOperands(LPred, RPred) == false)
@@ -9330,7 +9332,8 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
// Take SLT as an example: L0:x < L1:y and C <= 0
// ==> R0:(x -nsw y) < R1:(-C) is true
- if ((LPred == ICmpInst::ICMP_SLT || LPred == ICmpInst::ICMP_SLE) &&
+ if ((ICmpInst::isSigned(LPred) || LHasSameSign) &&
+ (ICmpInst::isLE(LPred) || ICmpInst::isLT(LPred)) &&
match(R0, m_NSWSub(m_Specific(L0), m_Specific(L1)))) {
if (match(R1, m_NonNegative()) &&
isImpliedCondMatchingOperands(LPred, RPred) == true)
diff --git a/llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll b/llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll
new file mode 100644
index 00000000000000..51f90f8e8fe8f8
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll
@@ -0,0 +1,215 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=instsimplify -S %s | FileCheck %s
+
+define i32 @gt_sub_nsw(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @gt_sub_nsw(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT: ret i32 [[ADD]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp = icmp samesign ugt i32 %x, %y ; x>y ? abs (x-y+1): 0
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub nsw i32 %x, %y
+ %add = add nsw i32 %sub, 1
+ %neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
+ %abscond = icmp samesign ult i32 %sub, -1
+ %abs = select i1 %abscond, i32 %neg, i32 %add
+ ret i32 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i32 0
+}
+
+define i32 @ge_sub_nsw(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @ge_sub_nsw(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign uge i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT: ret i32 [[ADD]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp = icmp samesign uge i32 %x, %y ; x>=y ? abs (x-y+1): 0
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub nsw i32 %x, %y
+ %add = add nsw i32 %sub, 1
+ %neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
+ %abscond = icmp samesign ult i32 %sub, -1
+ %abs = select i1 %abscond, i32 %neg, i32 %add
+ ret i32 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i32 0
+}
+
+define i8 @gt_sub_no_nsw(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @gt_sub_no_nsw(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[SUB]], 1
+; CHECK-NEXT: [[NEG:%.*]] = xor i8 [[SUB]], -1
+; CHECK-NEXT: [[ABSCOND:%.*]] = icmp samesign ult i8 [[SUB]], -1
+; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i8 [[NEG]], i8 [[ADD]]
+; CHECK-NEXT: ret i8 [[ABS]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ %cmp = icmp samesign ugt i8 %x, %y
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub i8 %x, %y
+ %add = add i8 %sub, 1
+ %neg = xor i8 %sub, -1
+ %abscond = icmp samesign ult i8 %sub, -1
+ %abs = select i1 %abscond, i8 %neg, i8 %add
+ ret i8 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i8 0
+}
+
+define i8 @ugt_sub_nsw(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @ugt_sub_nsw(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[SUB]], 1
+; CHECK-NEXT: [[NEG:%.*]] = xor i8 [[SUB]], -1
+; CHECK-NEXT: [[ABSCOND:%.*]] = icmp ult i8 [[SUB]], -1
+; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i8 [[NEG]], i8 [[ADD]]
+; CHECK-NEXT: ret i8 [[ABS]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ %cmp = icmp ugt i8 %x, %y
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub i8 %x, %y
+ %add = add i8 %sub, 1
+ %neg = xor i8 %sub, -1
+ %abscond = icmp ult i8 %sub, -1
+ %abs = select i1 %abscond, i8 %neg, i8 %add
+ ret i8 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i8 0
+}
+
+define i32 @lt_sub_nsw(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @lt_sub_nsw(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ult i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT: [[NEG:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT: ret i32 [[NEG]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp = icmp samesign ult i32 %x, %y ; x<y ? abs (x-y+1): 0
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub nsw i32 %x, %y
+ %add = add nsw i32 %sub, 1
+ %neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
+ %abscond = icmp samesign ugt i32 %sub, -1
+ %abs = select i1 %abscond, i32 %neg, i32 %add
+ ret i32 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i32 0
+}
+
+define i32 @le_sub_nsw(i32 %x, i32 %y) {
+; CHECK-LABEL: define i32 @le_sub_nsw(
+; CHECK-SAME: i32 [[X:%.*]], i32 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ule i32 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT: ret i32 [[ADD]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp = icmp samesign ule i32 %x, %y ; x<=y ? abs (x-y+1): 0
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub nsw i32 %x, %y
+ %add = add nsw i32 %sub, 1
+ %neg = xor i32 %sub, -1 ; sub nsw i32 0, %add
+ %abscond = icmp samesign ugt i32 %sub, -1
+ %abs = select i1 %abscond, i32 %neg, i32 %add
+ ret i32 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i32 0
+}
+
+define i8 @gt_sub_nsw_wrong_const(i8 %x, i8 %y) {
+; CHECK-LABEL: define i8 @gt_sub_nsw_wrong_const(
+; CHECK-SAME: i8 [[X:%.*]], i8 [[Y:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[CMP:%.*]] = icmp samesign ugt i8 [[X]], [[Y]]
+; CHECK-NEXT: br i1 [[CMP]], label %[[COND_TRUE:.*]], label %[[COND_END:.*]]
+; CHECK: [[COND_TRUE]]:
+; CHECK-NEXT: [[SUB:%.*]] = sub i8 [[X]], [[Y]]
+; CHECK-NEXT: [[ADD:%.*]] = add i8 [[SUB]], 2
+; CHECK-NEXT: [[NEG:%.*]] = xor i8 [[SUB]], -1
+; CHECK-NEXT: [[ABSCOND:%.*]] = icmp samesign ult i8 [[SUB]], -2
+; CHECK-NEXT: [[ABS:%.*]] = select i1 [[ABSCOND]], i8 [[NEG]], i8 [[ADD]]
+; CHECK-NEXT: ret i8 [[ABS]]
+; CHECK: [[COND_END]]:
+; CHECK-NEXT: ret i8 0
+;
+entry:
+ %cmp = icmp samesign ugt i8 %x, %y ; x>y ? abs (x-y+2): 0
+ br i1 %cmp, label %cond.true, label %cond.end
+
+cond.true: ; preds = %entry
+ %sub = sub i8 %x, %y
+ %add = add i8 %sub, 2 ; x-y+2
+ %neg = xor i8 %sub, -1 ; y-x-1
+ %neg1 = sub i8 %neg, 1 ; y-x-2
+ %abscond = icmp samesign ult i8 %sub, -2
+ %abs = select i1 %abscond, i8 %neg, i8 %add
+ ret i8 %abs
+
+cond.end: ; preds = %entry, %cond.true
+ ret i8 0
+}
|
nikic
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isImpliedCondition benefits little from the icmp samesign feature: the only routine that can use this information is isImpliedCondICmps when it is matching against signed predicates, since icmp samesign is already canonicalized with an unsigned predicate. Add this support.
Hm, why do other parts of isImpliedCond not benefit? I'd expect it to be useful at least for the isImpliedCondMatchingOperands and isImpliedCondOperands logic, possibly also isImpliedCondCommonOperandWithCR.
The code you're modifying here is a very special case -- it's probably exactly the one part of this code where I wouldn't bother with samesign support :)
I did look at isImpliedCondMatchingOperands, which just calls isImplied{True,False}ByMatchingCmp. Wouldn't icmp samesign be canonicalized with unsigned predicates, and wouldn't the predicates either match or have LT => LE | NE and GT => GE | NE? I also looked at isImpliedCondOperands, and due to the canonicalization, wouldn't LT + LE and GT + GE call isTruePredicate with LE + LE, and isn't the signed case in isTruePredicate strictly less powerful than the unsigned case? I'm not too sure about isImpliedCondCommonOperandWithCR, but my reasoning is that getting signed ConstantRanges versus getting unsigned ConstantRanges for the same Values, in the context of comparing ranges, would make no difference.
I realized this too, but this seems to be the only legitimate opportunity. |
|
You generally can't assume that both icmps will get the same canonicalization. Consider code structure like this: The first comparison can't be canonicalized, because the preconditions for that don't hold, so we only canonicalize the second one. And then we'd want to undo that one to a sge b to make the signedness of the predicates match, and then we have the implication that a sgt b => a sge b. |
|
Subsumed by #120120. |
isImpliedCondition benefits little from the icmp samesign feature: the only routine that can use this information is isImpliedCondICmps when it is matching against signed predicates, since icmp samesign is already canonicalized with an unsigned predicate. Add this support.