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

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Mon Nov 18 05:38:24 PST 2024


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

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.

>From de410817ea4cd3ed97c0cfae1b6747242c42efba Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <ramkumar.ramachandra at codasip.com>
Date: Mon, 18 Nov 2024 12:13:48 +0000
Subject: [PATCH] ValueTracking: teach implied-cond about samesign

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/lib/Analysis/ValueTracking.cpp           |   7 +-
 .../implied-condition-samesign.ll             | 215 ++++++++++++++++++
 2 files changed, 220 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll

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



More information about the llvm-commits mailing list