Skip to content

Commit 0f0c0c3

Browse files
authored
[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 #117107. Compile-time impact: http://llvm-compile-time-tracker.com/compare.php?from=69cc3f096ccbdef526bbd5a065a25c95122e87ee&to=919416d2c4c71e3b9fe533af2c168a36c7893be5&stat=instructions%3Au
1 parent 3433e41 commit 0f0c0c3

File tree

3 files changed

+249
-24
lines changed

3 files changed

+249
-24
lines changed

llvm/lib/Transforms/Scalar/ConstraintElimination.cpp

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,9 +1498,6 @@ static bool checkOrAndOpImpliedByOther(
14981498
FactOrCheck &CB, ConstraintInfo &Info, Module *ReproducerModule,
14991499
SmallVectorImpl<ReproducerEntry> &ReproducerCondStack,
15001500
SmallVectorImpl<StackEntry> &DFSInStack) {
1501-
1502-
CmpInst::Predicate Pred;
1503-
Value *A, *B;
15041501
Instruction *JoinOp = CB.getContextInst();
15051502
CmpInst *CmpToCheck = cast<CmpInst>(CB.getInstructionToSimplify());
15061503
unsigned OtherOpIdx = JoinOp->getOperand(0) == CmpToCheck ? 1 : 0;
@@ -1511,22 +1508,39 @@ static bool checkOrAndOpImpliedByOther(
15111508
if (OtherOpIdx != 0 && isa<SelectInst>(JoinOp))
15121509
return false;
15131510

1514-
if (!match(JoinOp->getOperand(OtherOpIdx),
1515-
m_ICmp(Pred, m_Value(A), m_Value(B))))
1516-
return false;
1517-
1518-
// For OR, check if the negated condition implies CmpToCheck.
1519-
bool IsOr = match(JoinOp, m_LogicalOr());
1520-
if (IsOr)
1521-
Pred = CmpInst::getInversePredicate(Pred);
1522-
1523-
// Optimistically add fact from first condition.
15241511
unsigned OldSize = DFSInStack.size();
1525-
Info.addFact(Pred, A, B, CB.NumIn, CB.NumOut, DFSInStack);
1512+
auto InfoRestorer = make_scope_exit([&]() {
1513+
// Remove entries again.
1514+
while (OldSize < DFSInStack.size()) {
1515+
StackEntry E = DFSInStack.back();
1516+
removeEntryFromStack(E, Info, ReproducerModule, ReproducerCondStack,
1517+
DFSInStack);
1518+
}
1519+
});
1520+
bool IsOr = match(JoinOp, m_LogicalOr());
1521+
SmallVector<Value *, 4> Worklist({JoinOp->getOperand(OtherOpIdx)});
1522+
// Do a traversal of the AND/OR tree to add facts from leaf compares.
1523+
while (!Worklist.empty()) {
1524+
Value *Val = Worklist.pop_back_val();
1525+
Value *LHS, *RHS;
1526+
ICmpInst::Predicate Pred;
1527+
if (match(Val, m_ICmp(Pred, m_Value(LHS), m_Value(RHS)))) {
1528+
// For OR, check if the negated condition implies CmpToCheck.
1529+
if (IsOr)
1530+
Pred = CmpInst::getInversePredicate(Pred);
1531+
// Optimistically add fact from the other compares in the AND/OR.
1532+
Info.addFact(Pred, LHS, RHS, CB.NumIn, CB.NumOut, DFSInStack);
1533+
continue;
1534+
}
1535+
if (IsOr ? match(Val, m_LogicalOr(m_Value(LHS), m_Value(RHS)))
1536+
: match(Val, m_LogicalAnd(m_Value(LHS), m_Value(RHS)))) {
1537+
Worklist.push_back(LHS);
1538+
Worklist.push_back(RHS);
1539+
}
1540+
}
15261541
if (OldSize == DFSInStack.size())
15271542
return false;
15281543

1529-
bool Changed = false;
15301544
// Check if the second condition can be simplified now.
15311545
if (auto ImpliedCondition =
15321546
checkCondition(CmpToCheck->getPredicate(), CmpToCheck->getOperand(0),
@@ -1540,16 +1554,10 @@ static bool checkOrAndOpImpliedByOther(
15401554
1 - OtherOpIdx,
15411555
ConstantInt::getBool(JoinOp->getType(), *ImpliedCondition));
15421556

1543-
Changed = true;
1557+
return true;
15441558
}
15451559

1546-
// Remove entries again.
1547-
while (OldSize < DFSInStack.size()) {
1548-
StackEntry E = DFSInStack.back();
1549-
removeEntryFromStack(E, Info, ReproducerModule, ReproducerCondStack,
1550-
DFSInStack);
1551-
}
1552-
return Changed;
1560+
return false;
15531561
}
15541562

15551563
void ConstraintInfo::addFact(CmpInst::Predicate Pred, Value *A, Value *B,

llvm/test/Transforms/ConstraintElimination/and-implied-by-operands.ll

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,4 +497,221 @@ entry:
497497
ret i1 %and
498498
}
499499

500+
define void @and_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
501+
; CHECK-LABEL: @and_tree_second_implies_first(
502+
; CHECK-NEXT: entry:
503+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
504+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
505+
; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP0]], [[CMP1]]
506+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
507+
; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
508+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
509+
; CHECK: if.then:
510+
; CHECK-NEXT: call void @side_effect()
511+
; CHECK-NEXT: br label [[RETURN]]
512+
; CHECK: return:
513+
; CHECK-NEXT: ret void
514+
;
515+
entry:
516+
%cmp0 = icmp sge i32 %v0, %v1
517+
%cmp1 = icmp sge i32 %v1, %v2
518+
%and1 = and i1 %cmp0, %cmp1
519+
%cmp2 = icmp slt i32 %v0, %v2
520+
%and2 = and i1 %cmp2, %and1
521+
br i1 %and2, label %if.then, label %return
522+
523+
if.then:
524+
call void @side_effect()
525+
br label %return
526+
527+
return:
528+
ret void
529+
}
530+
531+
define void @and_tree_second_implies_first_perm1(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
532+
; CHECK-LABEL: @and_tree_second_implies_first_perm1(
533+
; CHECK-NEXT: entry:
534+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
535+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
536+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
537+
; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP2]], [[CMP1]]
538+
; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
539+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
540+
; CHECK: if.then:
541+
; CHECK-NEXT: call void @side_effect()
542+
; CHECK-NEXT: br label [[RETURN]]
543+
; CHECK: return:
544+
; CHECK-NEXT: ret void
545+
;
546+
entry:
547+
%cmp0 = icmp sge i32 %v0, %v1
548+
%cmp1 = icmp sge i32 %v1, %v2
549+
%cmp2 = icmp slt i32 %v0, %v2
550+
%and1 = and i1 %cmp2, %cmp1
551+
%and2 = and i1 %cmp0, %and1
552+
br i1 %and2, label %if.then, label %return
553+
554+
if.then:
555+
call void @side_effect()
556+
br label %return
557+
558+
return:
559+
ret void
560+
}
561+
562+
563+
define void @and_tree_second_implies_first_perm2(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
564+
; CHECK-LABEL: @and_tree_second_implies_first_perm2(
565+
; CHECK-NEXT: entry:
566+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
567+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
568+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
569+
; CHECK-NEXT: [[AND1:%.*]] = and i1 [[CMP0]], [[CMP2]]
570+
; CHECK-NEXT: [[AND2:%.*]] = and i1 false, [[AND1]]
571+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
572+
; CHECK: if.then:
573+
; CHECK-NEXT: call void @side_effect()
574+
; CHECK-NEXT: br label [[RETURN]]
575+
; CHECK: return:
576+
; CHECK-NEXT: ret void
577+
;
578+
entry:
579+
%cmp0 = icmp sge i32 %v0, %v1
580+
%cmp1 = icmp sge i32 %v1, %v2
581+
%cmp2 = icmp slt i32 %v0, %v2
582+
%and1 = and i1 %cmp0, %cmp2
583+
%and2 = and i1 %cmp1, %and1
584+
br i1 %and2, label %if.then, label %return
585+
586+
if.then:
587+
call void @side_effect()
588+
br label %return
589+
590+
return:
591+
ret void
592+
}
593+
594+
define void @logical_and_tree_second_implies_first(i32 %v0, i32 %v1, i32 %v2) {
595+
; CHECK-LABEL: @logical_and_tree_second_implies_first(
596+
; CHECK-NEXT: entry:
597+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
598+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
599+
; CHECK-NEXT: [[AND1:%.*]] = select i1 [[CMP0]], i1 [[CMP1]], i1 false
600+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
601+
; CHECK-NEXT: [[AND2:%.*]] = select i1 [[CMP2]], i1 [[AND1]], i1 false
602+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
603+
; CHECK: if.then:
604+
; CHECK-NEXT: call void @side_effect()
605+
; CHECK-NEXT: br label [[RETURN]]
606+
; CHECK: return:
607+
; CHECK-NEXT: ret void
608+
;
609+
entry:
610+
%cmp0 = icmp sge i32 %v0, %v1
611+
%cmp1 = icmp sge i32 %v1, %v2
612+
%and1 = select i1 %cmp0, i1 %cmp1, i1 false
613+
%cmp2 = icmp slt i32 %v0, %v2
614+
%and2 = select i1 %cmp2, i1 %and1, i1 false
615+
br i1 %and2, label %if.then, label %return
616+
617+
if.then:
618+
call void @side_effect()
619+
br label %return
620+
621+
return:
622+
ret void
623+
}
624+
625+
define void @or_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
626+
; CHECK-LABEL: @or_tree_second_implies_first(
627+
; CHECK-NEXT: entry:
628+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
629+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
630+
; CHECK-NEXT: [[AND1:%.*]] = or i1 [[CMP0]], [[CMP1]]
631+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
632+
; CHECK-NEXT: [[AND2:%.*]] = or i1 true, [[AND1]]
633+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
634+
; CHECK: if.then:
635+
; CHECK-NEXT: call void @side_effect()
636+
; CHECK-NEXT: br label [[RETURN]]
637+
; CHECK: return:
638+
; CHECK-NEXT: ret void
639+
;
640+
entry:
641+
%cmp0 = icmp sge i32 %v0, %v1
642+
%cmp1 = icmp sge i32 %v1, %v2
643+
%and1 = or i1 %cmp0, %cmp1
644+
%cmp2 = icmp slt i32 %v0, %v2
645+
%and2 = or i1 %cmp2, %and1
646+
br i1 %and2, label %if.then, label %return
647+
648+
if.then:
649+
call void @side_effect()
650+
br label %return
651+
652+
return:
653+
ret void
654+
}
655+
656+
define void @or_tree_second_implies_first_with_unknown_cond(i64 %x, i1 %cond) {
657+
; CHECK-LABEL: @or_tree_second_implies_first_with_unknown_cond(
658+
; CHECK-NEXT: entry:
659+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i64 [[X:%.*]], 1
660+
; CHECK-NEXT: [[OR1:%.*]] = select i1 [[CMP1]], i1 [[COND:%.*]], i1 false
661+
; CHECK-NEXT: [[CMP2:%.*]] = icmp ult i64 [[X]], 2
662+
; CHECK-NEXT: [[OR2:%.*]] = select i1 [[OR1]], i1 false, i1 false
663+
; CHECK-NEXT: br i1 [[OR2]], label [[IF_THEN:%.*]], label [[IF_END:%.*]]
664+
; CHECK: if.then:
665+
; CHECK-NEXT: call void @side_effect()
666+
; CHECK-NEXT: br label [[IF_END]]
667+
; CHECK: if.end:
668+
; CHECK-NEXT: ret void
669+
;
670+
entry:
671+
%cmp1 = icmp ugt i64 %x, 1
672+
%or1 = select i1 %cmp1, i1 %cond, i1 false
673+
%cmp2 = icmp ult i64 %x, 2
674+
%or2 = select i1 %or1, i1 %cmp2, i1 false
675+
br i1 %or2, label %if.then, label %if.end
676+
677+
if.then:
678+
call void @side_effect()
679+
br label %if.end
680+
681+
if.end:
682+
ret void
683+
}
684+
685+
define void @negative_and_or_tree_second_implies_first(i32 noundef %v0, i32 noundef %v1, i32 noundef %v2) {
686+
; CHECK-LABEL: @negative_and_or_tree_second_implies_first(
687+
; CHECK-NEXT: entry:
688+
; CHECK-NEXT: [[CMP0:%.*]] = icmp sge i32 [[V0:%.*]], [[V1:%.*]]
689+
; CHECK-NEXT: [[CMP1:%.*]] = icmp sge i32 [[V1]], [[V2:%.*]]
690+
; CHECK-NEXT: [[AND1:%.*]] = or i1 [[CMP0]], [[CMP1]]
691+
; CHECK-NEXT: [[CMP2:%.*]] = icmp slt i32 [[V0]], [[V2]]
692+
; CHECK-NEXT: [[AND2:%.*]] = and i1 [[CMP2]], [[AND1]]
693+
; CHECK-NEXT: br i1 [[AND2]], label [[IF_THEN:%.*]], label [[RETURN:%.*]]
694+
; CHECK: if.then:
695+
; CHECK-NEXT: call void @side_effect()
696+
; CHECK-NEXT: br label [[RETURN]]
697+
; CHECK: return:
698+
; CHECK-NEXT: ret void
699+
;
700+
entry:
701+
%cmp0 = icmp sge i32 %v0, %v1
702+
%cmp1 = icmp sge i32 %v1, %v2
703+
%and1 = or i1 %cmp0, %cmp1
704+
%cmp2 = icmp slt i32 %v0, %v2
705+
%and2 = and i1 %cmp2, %and1
706+
br i1 %and2, label %if.then, label %return
707+
708+
if.then:
709+
call void @side_effect()
710+
br label %return
711+
712+
return:
713+
ret void
714+
}
715+
716+
declare void @side_effect()
500717
declare void @no_noundef(i1 noundef)

llvm/test/Transforms/ConstraintElimination/or.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ define i1 @test_or_chain_ule_1(i4 %x, i4 %y, i4 %z, i4 %a, i4 %b) {
124124
; CHECK-NEXT: [[C_3:%.*]] = icmp ule i4 2, [[X]]
125125
; CHECK-NEXT: [[C_4:%.*]] = icmp ule i4 2, [[A:%.*]]
126126
; CHECK-NEXT: [[OR_1:%.*]] = or i1 [[C_1]], [[C_2]]
127-
; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[OR_1]], [[C_3]]
127+
; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[OR_1]], true
128128
; CHECK-NEXT: [[OR_3:%.*]] = or i1 [[C_4]], [[OR_2]]
129129
; CHECK-NEXT: br i1 [[OR_3]], label [[BB1:%.*]], label [[EXIT:%.*]]
130130
; CHECK: bb1:

0 commit comments

Comments
 (0)