diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp index d03e3a0570cd3..4884c23f16e12 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 &ReproducerCondStack, SmallVectorImpl &DFSInStack) { - - CmpInst::Predicate Pred; - Value *A, *B; Instruction *JoinOp = CB.getContextInst(); CmpInst *CmpToCheck = cast(CB.getInstructionToSimplify()); unsigned OtherOpIdx = JoinOp->getOperand(0) == CmpToCheck ? 1 : 0; @@ -1511,22 +1508,39 @@ static bool checkOrAndOpImpliedByOther( if (OtherOpIdx != 0 && isa(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 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 6bbc73c9c996c..8bd0b4100cff9 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 01b8ca973efa5..b401d6f181369 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: