[llvm] ValueTracking: teach implied-cond about samesign (PR #116614)

via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 18 05:39:05 PST 2024


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-analysis

Author: Ramkumar Ramachandra (artagnon)

<details>
<summary>Changes</summary>

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
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
+}

``````````

</details>


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


More information about the llvm-commits mailing list