Skip to content

Conversation

@artagnon
Copy link
Contributor

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.

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.
@artagnon artagnon requested review from arsenm and dtcxzyw November 18, 2024 13:38
@artagnon artagnon requested a review from nikic as a code owner November 18, 2024 13:38
@llvmbot llvmbot added the llvm:analysis Includes value tracking, cost tables and constant folding label Nov 18, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 18, 2024

@llvm/pr-subscribers-llvm-analysis

Author: Ramkumar Ramachandra (artagnon)

Changes

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.


Full diff: https://github.com/llvm/llvm-project/pull/116614.diff

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+5-2)
  • (added) llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll (+215)
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
+}

Copy link
Contributor

@nikic nikic left a 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 :)

@artagnon
Copy link
Contributor Author

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.

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.

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 realized this too, but this seems to be the only legitimate opportunity.

@nikic
Copy link
Contributor

nikic commented Nov 18, 2024

You generally can't assume that both icmps will get the same canonicalization. Consider code structure like this:

if (a sgt b) {
  if (a sge 0 && b sge 0) {
    if (a sge b) { // canonicalized to a samesign uge b
    }
  }
}

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.

@artagnon
Copy link
Contributor Author

Subsumed by #120120.

@artagnon artagnon closed this Dec 17, 2024
@artagnon artagnon deleted the vt-samesign branch December 17, 2024 15:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

llvm:analysis Includes value tracking, cost tables and constant folding

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants