Skip to content

Commit eaa1b05

Browse files
authored
[LVI] Thread binop over select with constant arms (#110212)
Motivating case from https://github.com/delta-io/delta-rs: https://alive2.llvm.org/ce/z/3mzr4C
1 parent d6501dc commit eaa1b05

File tree

2 files changed

+141
-2
lines changed

2 files changed

+141
-2
lines changed

llvm/lib/Analysis/LazyValueInfo.cpp

+49-2
Original file line numberDiff line numberDiff line change
@@ -924,18 +924,65 @@ LazyValueInfoImpl::solveBlockValueBinaryOpImpl(
924924
Instruction *I, BasicBlock *BB,
925925
std::function<ConstantRange(const ConstantRange &, const ConstantRange &)>
926926
OpFn) {
927+
Value *LHS = I->getOperand(0);
928+
Value *RHS = I->getOperand(1);
929+
930+
auto ThreadBinOpOverSelect =
931+
[&](Value *X, const ConstantRange &CRX, SelectInst *Y,
932+
bool XIsLHS) -> std::optional<ValueLatticeElement> {
933+
Value *Cond = Y->getCondition();
934+
// Only handle selects with constant values.
935+
Constant *TrueC = dyn_cast<Constant>(Y->getTrueValue());
936+
if (!TrueC)
937+
return std::nullopt;
938+
Constant *FalseC = dyn_cast<Constant>(Y->getFalseValue());
939+
if (!FalseC)
940+
return std::nullopt;
941+
if (!isGuaranteedNotToBeUndef(Cond, AC))
942+
return std::nullopt;
943+
944+
ConstantRange TrueX =
945+
CRX.intersectWith(getValueFromCondition(X, Cond, /*CondIsTrue=*/true,
946+
/*UseBlockValue=*/false)
947+
->asConstantRange(X->getType()));
948+
ConstantRange FalseX =
949+
CRX.intersectWith(getValueFromCondition(X, Cond, /*CondIsTrue=*/false,
950+
/*UseBlockValue=*/false)
951+
->asConstantRange(X->getType()));
952+
ConstantRange TrueY = TrueC->toConstantRange();
953+
ConstantRange FalseY = FalseC->toConstantRange();
954+
955+
if (XIsLHS)
956+
return ValueLatticeElement::getRange(
957+
OpFn(TrueX, TrueY).unionWith(OpFn(FalseX, FalseY)));
958+
return ValueLatticeElement::getRange(
959+
OpFn(TrueY, TrueX).unionWith(OpFn(FalseY, FalseX)));
960+
};
961+
927962
// Figure out the ranges of the operands. If that fails, use a
928963
// conservative range, but apply the transfer rule anyways. This
929964
// lets us pick up facts from expressions like "and i32 (call i32
930965
// @foo()), 32"
931-
std::optional<ConstantRange> LHSRes = getRangeFor(I->getOperand(0), I, BB);
966+
std::optional<ConstantRange> LHSRes = getRangeFor(LHS, I, BB);
932967
if (!LHSRes)
933968
return std::nullopt;
934969

935-
std::optional<ConstantRange> RHSRes = getRangeFor(I->getOperand(1), I, BB);
970+
// Try to thread binop over rhs select
971+
if (auto *SI = dyn_cast<SelectInst>(RHS)) {
972+
if (auto Res = ThreadBinOpOverSelect(LHS, *LHSRes, SI, /*XIsLHS=*/true))
973+
return *Res;
974+
}
975+
976+
std::optional<ConstantRange> RHSRes = getRangeFor(RHS, I, BB);
936977
if (!RHSRes)
937978
return std::nullopt;
938979

980+
// Try to thread binop over lhs select
981+
if (auto *SI = dyn_cast<SelectInst>(LHS)) {
982+
if (auto Res = ThreadBinOpOverSelect(RHS, *RHSRes, SI, /*XIsLHS=*/false))
983+
return *Res;
984+
}
985+
939986
const ConstantRange &LHSRange = *LHSRes;
940987
const ConstantRange &RHSRange = *RHSRes;
941988
return ValueLatticeElement::getRange(OpFn(LHSRange, RHSRange));

llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll

+92
Original file line numberDiff line numberDiff line change
@@ -630,3 +630,95 @@ define i64 @test_shl_nsw_at_use(i64 noundef %x) {
630630
%res = select i1 %cmp, i64 %shr, i64 0
631631
ret i64 %res
632632
}
633+
634+
define i1 @test_icmp_mod(i64 noundef %x) {
635+
; CHECK-LABEL: @test_icmp_mod(
636+
; CHECK-NEXT: entry:
637+
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
638+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
639+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
640+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
641+
; CHECK-NEXT: ret i1 false
642+
;
643+
entry:
644+
%rem = srem i64 %x, 86400
645+
%cmp = icmp slt i64 %rem, 0
646+
%cond = select i1 %cmp, i64 86400, i64 0
647+
%add = add nsw i64 %cond, %rem
648+
%cmp1 = icmp ugt i64 %add, 86399
649+
ret i1 %cmp1
650+
}
651+
652+
define i1 @test_icmp_mod_commuted1(i64 noundef %x) {
653+
; CHECK-LABEL: @test_icmp_mod_commuted1(
654+
; CHECK-NEXT: entry:
655+
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
656+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
657+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
658+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[REM]], [[COND]]
659+
; CHECK-NEXT: ret i1 false
660+
;
661+
entry:
662+
%rem = srem i64 %x, 86400
663+
%cmp = icmp slt i64 %rem, 0
664+
%cond = select i1 %cmp, i64 86400, i64 0
665+
%add = add nsw i64 %rem, %cond
666+
%cmp1 = icmp ugt i64 %add, 86399
667+
ret i1 %cmp1
668+
}
669+
670+
define i1 @test_icmp_mod_commuted2(i64 noundef %x) {
671+
; CHECK-LABEL: @test_icmp_mod_commuted2(
672+
; CHECK-NEXT: entry:
673+
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
674+
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[REM]], -1
675+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 0, i64 86400
676+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
677+
; CHECK-NEXT: ret i1 false
678+
;
679+
entry:
680+
%rem = srem i64 %x, 86400
681+
%cmp = icmp sgt i64 %rem, -1
682+
%cond = select i1 %cmp, i64 0, i64 86400
683+
%add = add nsw i64 %cond, %rem
684+
%cmp1 = icmp ugt i64 %add, 86399
685+
ret i1 %cmp1
686+
}
687+
688+
define i1 @test_icmp_mod_undef(i64 %x) {
689+
; CHECK-LABEL: @test_icmp_mod_undef(
690+
; CHECK-NEXT: entry:
691+
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
692+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
693+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
694+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
695+
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i64 [[ADD]], 86399
696+
; CHECK-NEXT: ret i1 [[CMP1]]
697+
;
698+
entry:
699+
%rem = srem i64 %x, 86400
700+
%cmp = icmp slt i64 %rem, 0
701+
%cond = select i1 %cmp, i64 86400, i64 0
702+
%add = add nsw i64 %cond, %rem
703+
%cmp1 = icmp ugt i64 %add, 86399
704+
ret i1 %cmp1
705+
}
706+
707+
define i1 @test_icmp_mod_wrong_range(i64 noundef %x) {
708+
; CHECK-LABEL: @test_icmp_mod_wrong_range(
709+
; CHECK-NEXT: entry:
710+
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
711+
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
712+
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86401, i64 0
713+
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
714+
; CHECK-NEXT: [[CMP1:%.*]] = icmp samesign ugt i64 [[ADD]], 86399
715+
; CHECK-NEXT: ret i1 [[CMP1]]
716+
;
717+
entry:
718+
%rem = srem i64 %x, 86400
719+
%cmp = icmp slt i64 %rem, 0
720+
%cond = select i1 %cmp, i64 86401, i64 0
721+
%add = add nsw i64 %cond, %rem
722+
%cmp1 = icmp ugt i64 %add, 86399
723+
ret i1 %cmp1
724+
}

0 commit comments

Comments
 (0)