Skip to content

VT: teach isImpliedCondOperands about samesign #120263

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

Merged
merged 1 commit into from
Jan 10, 2025

Conversation

artagnon
Copy link
Contributor

@artagnon artagnon commented Dec 17, 2024

isImpliedCondICmps() and its callers in ValueTracking can greatly benefit from being taught about samesign. As a first step, teach one caller, namely isImpliedCondOperands(). Very minimal changes are required for this, as CmpPredicate::getMatching() does most of the work.

@llvmbot
Copy link
Member

llvmbot commented Dec 17, 2024

@llvm/pr-subscribers-llvm-analysis

Author: Ramkumar Ramachandra (artagnon)

Changes

isImpliedCondICmps() and its callers in ValueTracking can greatly benefit from being taught about samesign. As a first step, teach one caller, namely isImpliedCondOperands(). Very minimal changes are required for this, as CmpPredicate::getMatching() does most of the work.

-- 8< --
Based on #120257.


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

2 Files Affected:

  • (modified) llvm/lib/Analysis/ValueTracking.cpp (+13-15)
  • (added) llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll (+206)
diff --git a/llvm/lib/Analysis/ValueTracking.cpp b/llvm/lib/Analysis/ValueTracking.cpp
index a43f5b6cec2f4e..e704b4e09fb895 100644
--- a/llvm/lib/Analysis/ValueTracking.cpp
+++ b/llvm/lib/Analysis/ValueTracking.cpp
@@ -9223,8 +9223,8 @@ static bool isTruePredicate(CmpInst::Predicate Pred, const Value *LHS,
 /// Return true if "icmp Pred BLHS BRHS" is true whenever "icmp Pred
 /// ALHS ARHS" is true.  Otherwise, return std::nullopt.
 static std::optional<bool>
-isImpliedCondOperands(CmpInst::Predicate Pred, const Value *ALHS,
-                      const Value *ARHS, const Value *BLHS, const Value *BRHS) {
+isImpliedCondOperands(CmpPredicate Pred, const Value *ALHS, const Value *ARHS,
+                      const Value *BLHS, const Value *BRHS) {
   switch (Pred) {
   default:
     return std::nullopt;
@@ -9293,36 +9293,34 @@ static std::optional<bool> isImpliedCondCommonOperandWithCR(
 /// Return true if LHS implies RHS (expanded to its components as "R0 RPred R1")
 /// is true.  Return false if LHS implies RHS is false. Otherwise, return
 /// std::nullopt if we can't infer anything.
-static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
-                                              CmpInst::Predicate RPred,
-                                              const Value *R0, const Value *R1,
-                                              const DataLayout &DL,
-                                              bool LHSIsTrue) {
+static std::optional<bool>
+isImpliedCondICmps(const ICmpInst *LHS, CmpPredicate RPred, const Value *R0,
+                   const Value *R1, const DataLayout &DL, bool LHSIsTrue) {
   Value *L0 = LHS->getOperand(0);
   Value *L1 = LHS->getOperand(1);
 
   // The rest of the logic assumes the LHS condition is true.  If that's not the
   // case, invert the predicate to make it so.
-  CmpInst::Predicate LPred =
-      LHSIsTrue ? LHS->getPredicate() : LHS->getInversePredicate();
+  CmpPredicate LPred =
+      LHSIsTrue ? LHS->getCmpPredicate() : LHS->getInverseCmpPredicate();
 
   // We can have non-canonical operands, so try to normalize any common operand
   // to L0/R0.
   if (L0 == R1) {
     std::swap(R0, R1);
-    RPred = ICmpInst::getSwappedPredicate(RPred);
+    RPred = ICmpInst::getSwappedCmpPredicate(RPred);
   }
   if (R0 == L1) {
     std::swap(L0, L1);
-    LPred = ICmpInst::getSwappedPredicate(LPred);
+    LPred = ICmpInst::getSwappedCmpPredicate(LPred);
   }
   if (L1 == R1) {
     // If we have L0 == R0 and L1 == R1, then make L1/R1 the constants.
     if (L0 != R0 || match(L0, m_ImmConstant())) {
       std::swap(L0, L1);
-      LPred = ICmpInst::getSwappedPredicate(LPred);
+      LPred = ICmpInst::getSwappedCmpPredicate(LPred);
       std::swap(R0, R1);
-      RPred = ICmpInst::getSwappedPredicate(RPred);
+      RPred = ICmpInst::getSwappedCmpPredicate(RPred);
     }
   }
 
@@ -9381,8 +9379,8 @@ static std::optional<bool> isImpliedCondICmps(const ICmpInst *LHS,
       match(L0, m_c_Add(m_Specific(L1), m_Specific(R1))))
     return CmpPredicate::getMatching(LPred, RPred).has_value();
 
-  if (LPred == RPred)
-    return isImpliedCondOperands(LPred, L0, L1, R0, R1);
+  if (auto P = CmpPredicate::getMatching(LPred, RPred))
+    return isImpliedCondOperands(*P, L0, L1, R0, R1);
 
   return std::nullopt;
 }
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..62f8451788273e
--- /dev/null
+++ b/llvm/test/Analysis/ValueTracking/implied-condition-samesign.ll
@@ -0,0 +1,206 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt -passes=instsimplify -S %s | FileCheck %s
+
+define i1 @incr_sle(i32 %i, i32 %len) {
+; CHECK-LABEL: define i1 @incr_sle(
+; CHECK-SAME: i32 [[I:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %i.incr = add nsw nuw i32 %i, 1
+  %i.gt.len = icmp samesign ugt i32 %i, %len
+  %i.incr.sgt.len = icmp sgt i32 %i.incr, %len
+  %res = icmp sle i1 %i.incr.sgt.len, %i.gt.len
+  ret i1 %res
+}
+
+define i1 @incr_sge(i32 %i, i32 %len) {
+; CHECK-LABEL: define i1 @incr_sge(
+; CHECK-SAME: i32 [[I:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %i.incr = add nsw nuw i32 %i, 1
+  %i.lt.len = icmp samesign ult i32 %i, %len
+  %i.incr.slt.len = icmp slt i32 %i.incr, %len
+  %res = icmp sge i1 %i.incr.slt.len, %i.lt.len
+  ret i1 %res
+}
+
+define i1 @incr_ule(i32 %i, i32 %len) {
+; CHECK-LABEL: define i1 @incr_ule(
+; CHECK-SAME: i32 [[I:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %i.incr = add nsw nuw i32 %i, 1
+  %i.gt.len = icmp samesign ugt i32 %i, %len
+  %i.incr.sgt.len = icmp sgt i32 %i.incr, %len
+  %res = icmp ule i1 %i.gt.len, %i.incr.sgt.len
+  ret i1 %res
+}
+
+define i1 @incr_uge(i32 %i, i32 %len) {
+; CHECK-LABEL: define i1 @incr_uge(
+; CHECK-SAME: i32 [[I:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:    ret i1 true
+;
+  %i.incr = add nsw nuw i32 %i, 1
+  %i.lt.len = icmp samesign ult i32 %i, %len
+  %i.incr.slt.len = icmp slt i32 %i.incr, %len
+  %res = icmp uge i1 %i.lt.len, %i.incr.slt.len
+  ret i1 %res
+}
+
+define i1 @sgt_implies_ge_via_assume(i32 %i, i32 %j) {
+; CHECK-LABEL: define i1 @sgt_implies_ge_via_assume(
+; CHECK-SAME: i32 [[I:%.*]], i32 [[J:%.*]]) {
+; CHECK-NEXT:    [[I_SGT_J:%.*]] = icmp sgt i32 [[I]], [[J]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[I_SGT_J]])
+; CHECK-NEXT:    [[I_GE_J:%.*]] = icmp samesign uge i32 [[I]], [[J]]
+; CHECK-NEXT:    ret i1 [[I_GE_J]]
+;
+  %i.sgt.j = icmp sgt i32 %i, %j
+  call void @llvm.assume(i1 %i.sgt.j)
+  %i.ge.j = icmp samesign uge i32 %i, %j
+  ret i1 %i.ge.j
+}
+
+define i32 @gt_implies_sge_dominating(i32 %a, i32 %len) {
+; CHECK-LABEL: define i32 @gt_implies_sge_dominating(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_GT_LEN:%.*]] = icmp samesign ugt i32 [[A]], [[LEN]]
+; CHECK-NEXT:    br i1 [[A_GT_LEN]], label %[[TAKEN:.*]], label %[[END:.*]]
+; CHECK:       [[TAKEN]]:
+; CHECK-NEXT:    [[A_SGE_LEN:%.*]] = icmp sge i32 [[A]], [[LEN]]
+; CHECK-NEXT:    [[RES:%.*]] = select i1 [[A_SGE_LEN]], i32 30, i32 0
+; CHECK-NEXT:    ret i32 [[RES]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i32 -1
+;
+entry:
+  %a.gt.len = icmp samesign ugt i32 %a, %len
+  br i1 %a.gt.len, label %taken, label %end
+
+taken:
+  %a.sge.len = icmp sge i32 %a, %len
+  %res = select i1 %a.sge.len, i32 30, i32 0
+  ret i32 %res
+
+end:
+  ret i32 -1
+}
+
+define i32 @gt_implies_sge_dominating_cr(i32 %a, i32 %len) {
+; CHECK-LABEL: define i32 @gt_implies_sge_dominating_cr(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_GT_20:%.*]] = icmp samesign ugt i32 [[A]], 20
+; CHECK-NEXT:    br i1 [[A_GT_20]], label %[[TAKEN:.*]], label %[[END:.*]]
+; CHECK:       [[TAKEN]]:
+; CHECK-NEXT:    [[A_SGE_10:%.*]] = icmp sge i32 [[A]], 10
+; CHECK-NEXT:    [[RES:%.*]] = select i1 [[A_SGE_10]], i32 30, i32 0
+; CHECK-NEXT:    ret i32 [[RES]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i32 -1
+;
+entry:
+  %a.gt.20 = icmp samesign ugt i32 %a, 20
+  br i1 %a.gt.20, label %taken, label %end
+
+taken:
+  %a.sge.10 = icmp sge i32 %a, 10
+  %res = select i1 %a.sge.10, i32 30, i32 0
+  ret i32 %res
+
+end:
+  ret i32 -1
+}
+
+define i32 @sgt_implies_ge_dominating_cr(i32 %a, i32 %len) {
+; CHECK-LABEL: define i32 @sgt_implies_ge_dominating_cr(
+; CHECK-SAME: i32 [[A:%.*]], i32 [[LEN:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[A_SGT_MINUS_10:%.*]] = icmp sgt i32 [[A]], -10
+; CHECK-NEXT:    br i1 [[A_SGT_MINUS_10]], label %[[TAKEN:.*]], label %[[END:.*]]
+; CHECK:       [[TAKEN]]:
+; CHECK-NEXT:    [[A_GE_MINUS_20:%.*]] = icmp samesign uge i32 [[A]], -20
+; CHECK-NEXT:    [[RES:%.*]] = select i1 [[A_GE_MINUS_20]], i32 30, i32 0
+; CHECK-NEXT:    ret i32 [[RES]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i32 -1
+;
+entry:
+  %a.sgt.minus.10 = icmp sgt i32 %a, -10
+  br i1 %a.sgt.minus.10, label %taken, label %end
+
+taken:
+  %a.ge.minus.20 = icmp samesign uge i32 %a, -20
+  %res = select i1 %a.ge.minus.20, i32 30, i32 0
+  ret i32 %res
+
+end:
+  ret i32 -1
+}
+
+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:    [[X_GT_Y:%.*]] = icmp samesign ugt i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[X_GT_Y]], label %[[TAKEN:.*]], label %[[END:.*]]
+; CHECK:       [[TAKEN]]:
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[SUB]], -1
+; CHECK-NEXT:    [[ABSCOND:%.*]] = icmp samesign ult i32 [[SUB]], -1
+; CHECK-NEXT:    [[ABS:%.*]] = select i1 [[ABSCOND]], i32 [[NEG]], i32 [[ADD]]
+; CHECK-NEXT:    ret i32 [[ABS]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %x.gt.y = icmp samesign ugt i32 %x, %y
+  br i1 %x.gt.y, label %taken, label %end
+
+taken:
+  %sub = sub nsw i32 %x, %y
+  %add = add nsw i32 %sub, 1
+  %neg = xor i32 %sub, -1
+  %abscond = icmp samesign ult i32 %sub, -1
+  %abs = select i1 %abscond, i32 %neg, i32 %add
+  ret i32 %abs
+
+end:
+  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:    [[X_GE_Y:%.*]] = icmp samesign uge i32 [[X]], [[Y]]
+; CHECK-NEXT:    br i1 [[X_GE_Y]], label %[[TAKEN:.*]], label %[[END:.*]]
+; CHECK:       [[TAKEN]]:
+; CHECK-NEXT:    [[SUB:%.*]] = sub nsw i32 [[X]], [[Y]]
+; CHECK-NEXT:    [[ADD:%.*]] = add nsw i32 [[SUB]], 1
+; CHECK-NEXT:    [[NEG:%.*]] = xor i32 [[SUB]], -1
+; CHECK-NEXT:    [[ABSCOND:%.*]] = icmp samesign ult i32 [[SUB]], -1
+; CHECK-NEXT:    [[ABS:%.*]] = select i1 [[ABSCOND]], i32 [[NEG]], i32 [[ADD]]
+; CHECK-NEXT:    ret i32 [[ABS]]
+; CHECK:       [[END]]:
+; CHECK-NEXT:    ret i32 0
+;
+entry:
+  %x.ge.y = icmp samesign uge i32 %x, %y
+  br i1 %x.ge.y, label %taken, label %end
+
+taken:
+  %sub = sub nsw i32 %x, %y
+  %add = add nsw i32 %sub, 1
+  %neg = xor i32 %sub, -1
+  %abscond = icmp samesign ult i32 %sub, -1
+  %abs = select i1 %abscond, i32 %neg, i32 %add
+  ret i32 %abs
+
+end:
+  ret i32 0
+}

@artagnon artagnon force-pushed the vt-samesign-cond-operands branch from f4bfa4e to a50f3b5 Compare January 9, 2025 18:35
isImpliedCondICmps() and its callers in ValueTracking can greatly
benefit from being taught about samesign. As a first step, teach one
caller, namely isImpliedCondOperands(). Very minimal changes are
required for this, as CmpPredicate::getMatching() does most of the work.
@artagnon artagnon force-pushed the vt-samesign-cond-operands branch from a50f3b5 to a05f6a7 Compare January 9, 2025 20:31
@artagnon
Copy link
Contributor Author

Gentle ping.

Copy link
Member

@dtcxzyw dtcxzyw left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LG

@artagnon artagnon merged commit b53e794 into llvm:main Jan 10, 2025
8 checks passed
@artagnon artagnon deleted the vt-samesign-cond-operands branch January 10, 2025 12:08
BaiXilin pushed a commit to BaiXilin/llvm-fix-vnni-instr-types that referenced this pull request Jan 12, 2025
isImpliedCondICmps() and its callers in ValueTracking can greatly
benefit from being taught about samesign. As a first step, teach one
caller, namely isImpliedCondOperands(). Very minimal changes are
required for this, as CmpPredicate::getMatching() does most of the work.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants