-
Notifications
You must be signed in to change notification settings - Fork 13.6k
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
Conversation
@llvm/pr-subscribers-llvm-analysis Author: Ramkumar Ramachandra (artagnon) ChangesisImpliedCondICmps() 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< -- Full diff: https://github.com/llvm/llvm-project/pull/120263.diff 2 Files Affected:
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
+}
|
f4bfa4e
to
a50f3b5
Compare
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.
a50f3b5
to
a05f6a7
Compare
Gentle ping. |
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.
LG
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.
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.