[llvm] 0f0c0c3 - [ConstraintElim] Extend `checkOrAndOpImpliedByOther` to handle and/or expr trees. (#117123)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Nov 26 17:04:55 PST 2024
Author: Yingwei Zheng
Date: 2024-11-27T09:04:52+08:00
New Revision: 0f0c0c36e3e90b4cb04004ed9c930f3863a36422
URL: https://github.com/llvm/llvm-project/commit/0f0c0c36e3e90b4cb04004ed9c930f3863a36422
DIFF: https://github.com/llvm/llvm-project/commit/0f0c0c36e3e90b4cb04004ed9c930f3863a36422.diff
LOG: [ConstraintElim] Extend `checkOrAndOpImpliedByOther` to handle and/or expr trees. (#117123)
This patch extends `checkOrAndOpImpliedByOther` to handle and/or trees.
Limitation: At least one of the operands of root and/or instruction
should be an icmp. That is, this patch doesn't support expressions like
`(cmp1 & cmp2) & (cmp3 & cmp4)`.
Closes https://github.com/llvm/llvm-project/issues/117107.
Compile-time impact:
http://llvm-compile-time-tracker.com/compare.php?from=69cc3f096ccbdef526bbd5a065a25c95122e87ee&to=919416d2c4c71e3b9fe533af2c168a36c7893be5&stat=instructions%3Au
Added:
Modified:
llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
llvm/test/Transforms/ConstraintElimination/or.ll
Removed:
################################################################################
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index d03e3a0570cd3f..4884c23f16e12a 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1498,9 +1498,6 @@ static bool checkOrAndOpImpliedByOther(
FactOrCheck &CB, ConstraintInfo &Info, Module *ReproducerModule,
SmallVectorImpl<ReproducerEntry> &ReproducerCondStack,
SmallVectorImpl<StackEntry> &DFSInStack) {
-
- CmpInst::Predicate Pred;
- Value *A, *B;
Instruction *JoinOp = CB.getContextInst();
CmpInst *CmpToCheck = cast<CmpInst>(CB.getInstructionToSimplify());
unsigned OtherOpIdx = JoinOp->getOperand(0) == CmpToCheck ? 1 : 0;
@@ -1511,22 +1508,39 @@ static bool checkOrAndOpImpliedByOther(
if (OtherOpIdx != 0 && isa<SelectInst>(JoinOp))
return false;
- if (!match(JoinOp->getOperand(OtherOpIdx),
- m_ICmp(Pred, m_Value(A), m_Value(B))))
- return false;
-
- // For OR, check if the negated condition implies CmpToCheck.
- bool IsOr = match(JoinOp, m_LogicalOr());
- if (IsOr)
- Pred = CmpInst::getInversePredicate(Pred);
-
- // Optimistically add fact from first condition.
unsigned OldSize = DFSInStack.size();
- Info.addFact(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack);
+ auto InfoRestorer = make_scope_exit([&]() {
+ // Remove entries again.
+ while (OldSize < DFSInStack.size()) {
+ StackEntry E = DFSInStack.back();
+ removeEntryFromStack(E, Info, ReproducerModule, ReproducerCondStack,
+ DFSInStack);
+ }
+ });
+ bool IsOr = match(JoinOp, m_LogicalOr());
+ SmallVector<Value *, 4> Worklist({JoinOp->getOperand(OtherOpIdx)});
+ // Do a traversal of the AND/OR tree to add facts from leaf compares.
+ while (!Worklist.empty()) {
+ Value *Val = Worklist.pop_back_val();
+ Value *LHS, *RHS;
+ ICmpInst::Predicate Pred;
+ if (match(Val, m_ICmp(Pred, m_Value(LHS), m_Value(RHS)))) {
+ // For OR, check if the negated condition implies CmpToCheck.
+ if (IsOr)
+ Pred = CmpInst::getInversePredicate(Pred);
+ // Optimistically add fact from the other compares in the AND/OR.
+ Info.addFact(Pred, LHS, RHS, CB.NumIn, CB.NumOut, DFSInStack);
+ continue;
+ }
+ if (IsOr ? match(Val, m_LogicalOr(m_Value(LHS), m_Value(RHS)))
+ : match(Val, m_LogicalAnd(m_Value(LHS), m_Value(RHS)))) {
+ Worklist.push_back(LHS);
+ Worklist.push_back(RHS);
+ }
+ }
if (OldSize == DFSInStack.size())
return false;
- bool Changed = false;
// Check if the second condition can be simplified now.
if (auto ImpliedCondition =
checkCondition(CmpToCheck->getPredicate(), CmpToCheck->getOperand(0),
@@ -1540,16 +1554,10 @@ static bool checkOrAndOpImpliedByOther(
1 - OtherOpIdx,
ConstantInt::getBool(JoinOp->getType(), *ImpliedCondition));
- Changed = true;
+ return true;
}
- // Remove entries again.
- while (OldSize < DFSInStack.size()) {
- StackEntry E = DFSInStack.back();
- removeEntryFromStack(E, Info, ReproducerModule, ReproducerCondStack,
- DFSInStack);
- }
- return Changed;
+ return false;
}
void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B,
diff --git a/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll b/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
index 6bbc73c9c996c8..8bd0b4100cff9f 100644
--- a/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
+++ b/llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
@@ -497,4 +497,221 @@ entry:
ret i1 %and
}
+define void @and_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: @and_tree_second_implies_first(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP0]], [[CMP1]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %and1 = and i1 %cmp0, %cmp1
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and2 = and i1 %cmp2, %and1
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+define void @and_tree_second_implies_first_perm1(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: @and_tree_second_implies_first_perm1(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP2]], [[CMP1]]
+; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and1 = and i1 %cmp2, %cmp1
+ %and2 = and i1 %cmp0, %and1
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+
+define void @and_tree_second_implies_first_perm2(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: @and_tree_second_implies_first_perm2(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP0]], [[CMP2]]
+; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and1 = and i1 %cmp0, %cmp2
+ %and2 = and i1 %cmp1, %and1
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+define void @logical_and_tree_second_implies_first(i32 %v0, i32 %v1, i32 %v2) {
+; CHECK-LABEL: @logical_and_tree_second_implies_first(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[AND1:%.*]] = select i1 [[CMP0]], i1 [[CMP1]], i1 false
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND2:%.*]] = select i1 [[CMP2]], i1 [[AND1]], i1 false
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %and1 = select i1 %cmp0, i1 %cmp1, i1 false
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and2 = select i1 %cmp2, i1 %and1, i1 false
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+define void @or_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: @or_tree_second_implies_first(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[AND1:%.*]] = or i1 [[CMP0]], [[CMP1]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND2:%.*]] = or i1 true, [[AND1]]
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %and1 = or i1 %cmp0, %cmp1
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and2 = or i1 %cmp2, %and1
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+define void @or_tree_second_implies_first_with_unknown_cond(i64 %x, i1 %cond) {
+; CHECK-LABEL: @or_tree_second_implies_first_with_unknown_cond(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i64 [[X:%.*]], 1
+; CHECK-NEXT: [[OR1:%.*]] = select i1 [[CMP1]], i1 [[COND:%.*]], i1 false
+; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i64 [[X]], 2
+; CHECK-NEXT: [[OR2:%.*]] = select i1 [[OR1]], i1 false, i1 false
+; CHECK-NEXT: br i1 [[OR2]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[IF_END]]
+; CHECK: if.end:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp1 = icmp ugt i64 %x, 1
+ %or1 = select i1 %cmp1, i1 %cond, i1 false
+ %cmp2 = icmp ult i64 %x, 2
+ %or2 = select i1 %or1, i1 %cmp2, i1 false
+ br i1 %or2, label %if.then, label %if.end
+
+if.then:
+ call void @side_effect()
+ br label %if.end
+
+if.end:
+ ret void
+}
+
+define void @negative_and_or_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
+; CHECK-LABEL: @negative_and_or_tree_second_implies_first(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
+; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
+; CHECK-NEXT: [[AND1:%.*]] = or i1 [[CMP0]], [[CMP1]]
+; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
+; CHECK-NEXT: [[AND2:%.*]] = and i1 [[CMP2]], [[AND1]]
+; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
+; CHECK: if.then:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: br label [[RETURN]]
+; CHECK: return:
+; CHECK-NEXT: ret void
+;
+entry:
+ %cmp0 = icmp sge i32 %v0, %v1
+ %cmp1 = icmp sge i32 %v1, %v2
+ %and1 = or i1 %cmp0, %cmp1
+ %cmp2 = icmp slt i32 %v0, %v2
+ %and2 = and i1 %cmp2, %and1
+ br i1 %and2, label %if.then, label %return
+
+if.then:
+ call void @side_effect()
+ br label %return
+
+return:
+ ret void
+}
+
+declare void @side_effect()
declare void @no_noundef(i1 noundef)
diff --git a/llvm/test/Transforms/ConstraintElimination/or.ll b/llvm/test/Transforms/ConstraintElimination/or.ll
index 01b8ca973efa56..b401d6f1813695 100644
--- a/llvm/test/Transforms/ConstraintElimination/or.ll
+++ b/llvm/test/Transforms/ConstraintElimination/or.ll
@@ -124,7 +124,7 @@ define i1 @test_or_chain_ule_1(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 2, [[X]]
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 2, [[A:%.*]]
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
-; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[OR_1]], [[C_3]]
+; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[OR_1]], true
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[C_4]], [[OR_2]]
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
; CHECK: bb1:
More information about the llvm-commits
mailing list