[llvm] [ConstraintElim] Simplify `MinMaxIntrinsic` (PR #75306)
Yingwei Zheng via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 13 01:06:55 PST 2023
https://github.com/dtcxzyw created https://github.com/llvm/llvm-project/pull/75306
This patch replaces min/max intrinsic with one of its operands if possible.
Fixes #75155.
>From d4478273e0f5fa14fdae00b99ac7013b3b4acb81 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 13 Dec 2023 16:42:21 +0800
Subject: [PATCH 1/2] [ConstraintElim] Add pre-commit tests for PR75155. NFC.
---
.../ConstraintElimination/minmax.ll | 145 ++++++++++++++++++
1 file changed, 145 insertions(+)
diff --git a/llvm/test/Transforms/ConstraintElimination/minmax.ll b/llvm/test/Transforms/ConstraintElimination/minmax.ll
index a31cf6845ad67d..a0392db4966a64 100644
--- a/llvm/test/Transforms/ConstraintElimination/minmax.ll
+++ b/llvm/test/Transforms/ConstraintElimination/minmax.ll
@@ -341,6 +341,151 @@ end:
ret i32 0
}
+; Test from PR75155
+define i32 @simplify_smax_val(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_smax_val
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smax.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp slt i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nsw i32 %a, 1
+ %max = call i32 @llvm.smax.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
+define i32 @simplify_umax_val(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_umax_val
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp ult i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nuw i32 %a, 1
+ %max = call i32 @llvm.umax.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
+define i32 @simplify_smin_val(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_smin_val
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smin.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp slt i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nsw i32 %a, 1
+ %max = call i32 @llvm.smin.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
+define i32 @simplify_umin_val(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_umin_val
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.umin.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp ult i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nuw i32 %a, 1
+ %max = call i32 @llvm.umin.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
+define i32 @simplify_smax_val_fail1(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_smax_val_fail1
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp slt i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 2
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smax.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp slt i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nsw i32 %a, 2
+ %max = call i32 @llvm.smax.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
+define i32 @simplify_smax_val_fail2(i32 %a, i32 %b) {
+; CHECK-LABEL: define i32 @simplify_smax_val_fail2
+; CHECK-SAME: (i32 [[A:%.*]], i32 [[B:%.*]]) {
+; CHECK-NEXT: start:
+; CHECK-NEXT: [[CMP:%.*]] = icmp ult i32 [[A]], [[B]]
+; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
+; CHECK: then:
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
+; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smax.i32(i32 [[B]], i32 [[ADD]])
+; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK: else:
+; CHECK-NEXT: ret i32 -1
+;
+start:
+ %cmp = icmp ult i32 %a, %b
+ br i1 %cmp, label %then, label %else
+then:
+ %add = add nsw i32 %a, 1
+ %max = call i32 @llvm.smax.i32(i32 %b, i32 %add)
+ ret i32 %max
+else:
+ ret i32 -1
+}
+
declare i32 @llvm.smin.i32(i32, i32)
declare i32 @llvm.smax.i32(i32, i32)
declare i32 @llvm.umin.i32(i32, i32)
>From d8ce6177d052f4ffefb7bd0c7847167cd15e37b2 Mon Sep 17 00:00:00 2001
From: Yingwei Zheng <dtcxzyw2333 at gmail.com>
Date: Wed, 13 Dec 2023 16:58:09 +0800
Subject: [PATCH 2/2] [ConstraintElim] Simplify `MinMaxIntrinsic`
---
.../Scalar/ConstraintElimination.cpp | 77 ++++++++++++++++---
.../Transforms/ConstraintElimination/debug.ll | 6 +-
.../ConstraintElimination/minmax.ll | 12 +--
.../reproducer-remarks-debug.ll | 2 +-
4 files changed, 73 insertions(+), 24 deletions(-)
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index fafbe17583f5da..6a991f694476c8 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1009,6 +1009,13 @@ void State::addInfoFor(BasicBlock &BB) {
if (isa<MinMaxIntrinsic>(&I)) {
WorkList.push_back(FactOrCheck::getInstFact(DT.getNode(&BB), &I));
+ for (Use &U : I.uses()) {
+ auto *UserI = getContextInstForUse(U);
+ auto *DTN = DT.getNode(UserI->getParent());
+ if (!DTN)
+ continue;
+ WorkList.push_back(FactOrCheck::getCheck(DTN, &U));
+ }
continue;
}
@@ -1260,14 +1267,12 @@ static void generateReproducer(CmpInst *Cond, Module *M,
assert(!verifyFunction(*F, &dbgs()));
}
-static std::optional<bool> checkCondition(CmpInst *Cmp, ConstraintInfo &Info,
+static std::optional<bool> checkCondition(CmpInst::Predicate Pred, Value *A,
+ Value *B, ConstraintInfo &Info,
unsigned NumIn, unsigned NumOut,
Instruction *ContextInst) {
- LLVM_DEBUG(dbgs() << "Checking " << *Cmp << "\n");
-
- CmpInst::Predicate Pred = Cmp->getPredicate();
- Value *A = Cmp->getOperand(0);
- Value *B = Cmp->getOperand(1);
+ LLVM_DEBUG(dbgs() << "Checking " << CmpInst::getPredicateName(Pred) << " "
+ << *A << ", " << *B << "\n");
auto R = Info.getConstraintForSolving(Pred, A, B);
if (R.empty() || !R.isValid(Info)){
@@ -1293,9 +1298,10 @@ static std::optional<bool> checkCondition(CmpInst *Cmp, ConstraintInfo &Info,
LLVM_DEBUG({
if (*ImpliedCondition) {
- dbgs() << "Condition " << *Cmp;
+ dbgs() << "Condition " << CmpInst::getPredicateName(Pred) << " " << *A
+ << ", " << *B;
} else {
- auto InversePred = Cmp->getInversePredicate();
+ auto InversePred = CmpInst::getInversePredicate(Pred);
dbgs() << "Condition " << CmpInst::getPredicateName(InversePred) << " "
<< *A << ", " << *B;
}
@@ -1339,11 +1345,53 @@ static bool checkAndReplaceCondition(
};
if (auto ImpliedCondition =
- checkCondition(Cmp, Info, NumIn, NumOut, ContextInst))
+ checkCondition(Cmp->getPredicate(), Cmp->getOperand(0),
+ Cmp->getOperand(1), Info, NumIn, NumOut, ContextInst))
return ReplaceCmpWithConstant(Cmp, *ImpliedCondition);
return false;
}
+static bool checkAndReplaceMinMax(MinMaxIntrinsic *MinMax, ConstraintInfo &Info,
+ unsigned NumIn, unsigned NumOut,
+ Instruction *ContextInst,
+ Module *ReproducerModule,
+ ArrayRef<ReproducerEntry> ReproducerCondStack,
+ DominatorTree &DT,
+ SmallVectorImpl<Instruction *> &ToRemove) {
+ auto ReplaceMinMaxWithOperand = [&](MinMaxIntrinsic *MinMax, bool UseLHS) {
+ // TODO: generate reproducer for min/max.
+ MinMax->replaceUsesWithIf(MinMax->getOperand(UseLHS ? 0 : 1),
+ [&DT, NumIn, NumOut, ContextInst](Use &U) {
+ auto *UserI = getContextInstForUse(U);
+ auto *DTN = DT.getNode(UserI->getParent());
+ if (!DTN || DTN->getDFSNumIn() < NumIn ||
+ DTN->getDFSNumOut() > NumOut)
+ return false;
+ if (UserI->getParent() ==
+ ContextInst->getParent() &&
+ UserI->comesBefore(ContextInst))
+ return false;
+
+ return true;
+ });
+ NumCondsRemoved++;
+ if (MinMax->use_empty())
+ ToRemove.push_back(MinMax);
+ return true;
+ };
+
+ if (auto ImpliedCondition = checkCondition(
+ MinMax->getPredicate(), MinMax->getOperand(0), MinMax->getOperand(1),
+ Info, NumIn, NumOut, ContextInst))
+ return ReplaceMinMaxWithOperand(MinMax, *ImpliedCondition);
+ if (auto ImpliedCondition = checkCondition(
+ ICmpInst::getNonStrictPredicate(MinMax->getPredicate()),
+ MinMax->getOperand(0), MinMax->getOperand(1), Info, NumIn, NumOut,
+ ContextInst))
+ return ReplaceMinMaxWithOperand(MinMax, *ImpliedCondition);
+ return false;
+}
+
static void
removeEntryFromStack(const StackEntry &E, ConstraintInfo &Info,
Module *ReproducerModule,
@@ -1380,9 +1428,10 @@ static bool checkAndSecondOpImpliedByFirst(
bool Changed = false;
// Check if the second condition can be simplified now.
- if (auto ImpliedCondition =
- checkCondition(cast<ICmpInst>(And->getOperand(1)), Info, CB.NumIn,
- CB.NumOut, CB.getContextInst())) {
+ ICmpInst *Cmp = cast<ICmpInst>(And->getOperand(1));
+ if (auto ImpliedCondition = checkCondition(
+ Cmp->getPredicate(), Cmp->getOperand(0), Cmp->getOperand(1), Info,
+ CB.NumIn, CB.NumOut, CB.getContextInst())) {
And->setOperand(1, ConstantInt::getBool(And->getType(), *ImpliedCondition));
Changed = true;
}
@@ -1611,6 +1660,10 @@ static bool eliminateConstraints(Function &F, DominatorTree &DT, LoopInfo &LI,
ReproducerCondStack, DFSInStack);
}
Changed |= Simplified;
+ } else if (auto *MinMax = dyn_cast<MinMaxIntrinsic>(Inst)) {
+ Changed |= checkAndReplaceMinMax(
+ MinMax, Info, CB.NumIn, CB.NumOut, CB.getContextInst(),
+ ReproducerModule.get(), ReproducerCondStack, S.DT, ToRemove);
}
continue;
}
diff --git a/llvm/test/Transforms/ConstraintElimination/debug.ll b/llvm/test/Transforms/ConstraintElimination/debug.ll
index f3f0f5056135c2..a6ab609752a831 100644
--- a/llvm/test/Transforms/ConstraintElimination/debug.ll
+++ b/llvm/test/Transforms/ConstraintElimination/debug.ll
@@ -11,8 +11,8 @@ define i1 @test_and_ule(i4 %x, i4 %y, i4 %z) {
; CHECK-NEXT: Adding 'ule %y, %z'
; CHECK-NEXT: constraint: %y + -1 * %z <= 0
-; CHECK: Checking %t.1 = icmp ule i4 %x, %z
-; CHECK: Condition %t.1 = icmp ule i4 %x, %z implied by dominating constraints
+; CHECK: Checking ule i4 %x, i4 %z
+; CHECK: Condition ule i4 %x, i4 %z implied by dominating constraints
; CHECK: Removing %y + -1 * %z <= 0
; CHECK: Removing %x + -1 * %y <= 0
@@ -41,7 +41,7 @@ define i1 @test_and_ugt(i4 %x, i4 %y, i4 %z) {
; CHECK-NEXT: Adding 'ugt %y, %z'
; CHECK-NEXT: constraint: -1 * %y + %z <= -1
-; CHECK: Checking %f.1 = icmp ule i4 %x, %z
+; CHECK: Checking ule i4 %x, i4 %z
; CHECK: Condition ugt i4 %x, i4 %z implied by dominating constraints
; CHECK: Removing -1 * %y + %z <= -1
diff --git a/llvm/test/Transforms/ConstraintElimination/minmax.ll b/llvm/test/Transforms/ConstraintElimination/minmax.ll
index a0392db4966a64..44751549acfad4 100644
--- a/llvm/test/Transforms/ConstraintElimination/minmax.ll
+++ b/llvm/test/Transforms/ConstraintElimination/minmax.ll
@@ -350,8 +350,7 @@ define i32 @simplify_smax_val(i32 %a, i32 %b) {
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
-; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smax.i32(i32 [[B]], i32 [[ADD]])
-; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK-NEXT: ret i32 [[B]]
; CHECK: else:
; CHECK-NEXT: ret i32 -1
;
@@ -374,8 +373,7 @@ define i32 @simplify_umax_val(i32 %a, i32 %b) {
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
-; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.umax.i32(i32 [[B]], i32 [[ADD]])
-; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK-NEXT: ret i32 [[B]]
; CHECK: else:
; CHECK-NEXT: ret i32 -1
;
@@ -398,8 +396,7 @@ define i32 @simplify_smin_val(i32 %a, i32 %b) {
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[A]], 1
-; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.smin.i32(i32 [[B]], i32 [[ADD]])
-; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK-NEXT: ret i32 [[ADD]]
; CHECK: else:
; CHECK-NEXT: ret i32 -1
;
@@ -422,8 +419,7 @@ define i32 @simplify_umin_val(i32 %a, i32 %b) {
; CHECK-NEXT: br i1 [[CMP]], label [[THEN:%.*]], label [[ELSE:%.*]]
; CHECK: then:
; CHECK-NEXT: [[ADD:%.*]] = add nuw i32 [[A]], 1
-; CHECK-NEXT: [[MAX:%.*]] = call i32 @llvm.umin.i32(i32 [[B]], i32 [[ADD]])
-; CHECK-NEXT: ret i32 [[MAX]]
+; CHECK-NEXT: ret i32 [[ADD]]
; CHECK: else:
; CHECK-NEXT: ret i32 -1
;
diff --git a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks-debug.ll b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks-debug.ll
index b8343aed8b4af7..b4b2e2ee7077ee 100644
--- a/llvm/test/Transforms/ConstraintElimination/reproducer-remarks-debug.ll
+++ b/llvm/test/Transforms/ConstraintElimination/reproducer-remarks-debug.ll
@@ -4,7 +4,7 @@
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
-; CHECK: Condition %c.2 = icmp eq ptr %a, null implied by dominating constraints
+; CHECK: Condition eq ptr %a, ptr null implied by dominating constraints
; CHECK-NEXT: %a <= 0
; CHECK-NEXT: Creating reproducer for %c.2 = icmp eq ptr %a, null
; CHECK-NEXT: found external input ptr %a
More information about the llvm-commits
mailing list