Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 31 additions & 23 deletions llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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),
Expand All @@ -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,
Expand Down
217 changes: 217 additions & 0 deletions llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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)
2 changes: 1 addition & 1 deletion llvm/test/Transforms/ConstraintElimination/or.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down