Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
56 changes: 33 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,41 @@ 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);
continue;
}
return false;
}
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 +1556,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
188 changes: 188 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,192 @@ 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 @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
Loading