Skip to content

[LVI] Thread binop over select with constant arms #110212

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Dec 9, 2024
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
51 changes: 49 additions & 2 deletions llvm/lib/Analysis/LazyValueInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -924,18 +924,65 @@ LazyValueInfoImpl::solveBlockValueBinaryOpImpl(
Instruction *I, BasicBlock *BB,
std::function<ConstantRange(const ConstantRange &, const ConstantRange &)>
OpFn) {
Value *LHS = I->getOperand(0);
Value *RHS = I->getOperand(1);

auto ThreadBinOpOverSelect =
[&](Value *X, const ConstantRange &CRX, SelectInst *Y,
bool XIsLHS) -> std::optional<ValueLatticeElement> {
Value *Cond = Y->getCondition();
// Only handle selects with constant values.
Constant *TrueC = dyn_cast<Constant>(Y->getTrueValue());
if (!TrueC)
return std::nullopt;
Constant *FalseC = dyn_cast<Constant>(Y->getFalseValue());
if (!FalseC)
return std::nullopt;
if (!isGuaranteedNotToBeUndef(Cond, AC))
return std::nullopt;

ConstantRange TrueX =
CRX.intersectWith(getValueFromCondition(X, Cond, /*CondIsTrue=*/true,
/*UseBlockValue=*/false)
->asConstantRange(X->getType()));
ConstantRange FalseX =
CRX.intersectWith(getValueFromCondition(X, Cond, /*CondIsTrue=*/false,
/*UseBlockValue=*/false)
->asConstantRange(X->getType()));
ConstantRange TrueY = TrueC->toConstantRange();
ConstantRange FalseY = FalseC->toConstantRange();

if (XIsLHS)
return ValueLatticeElement::getRange(
OpFn(TrueX, TrueY).unionWith(OpFn(FalseX, FalseY)));
return ValueLatticeElement::getRange(
OpFn(TrueY, TrueX).unionWith(OpFn(FalseY, FalseX)));
};

// Figure out the ranges of the operands. If that fails, use a
// conservative range, but apply the transfer rule anyways. This
// lets us pick up facts from expressions like "and i32 (call i32
// @foo()), 32"
std::optional<ConstantRange> LHSRes = getRangeFor(I->getOperand(0), I, BB);
std::optional<ConstantRange> LHSRes = getRangeFor(LHS, I, BB);
if (!LHSRes)
return std::nullopt;

std::optional<ConstantRange> RHSRes = getRangeFor(I->getOperand(1), I, BB);
// Try to thread binop over rhs select
if (auto *SI = dyn_cast<SelectInst>(RHS)) {
if (auto Res = ThreadBinOpOverSelect(LHS, *LHSRes, SI, /*XIsLHS=*/true))
return *Res;
}

std::optional<ConstantRange> RHSRes = getRangeFor(RHS, I, BB);
if (!RHSRes)
return std::nullopt;

// Try to thread binop over lhs select
if (auto *SI = dyn_cast<SelectInst>(LHS)) {
if (auto Res = ThreadBinOpOverSelect(RHS, *RHSRes, SI, /*XIsLHS=*/false))
return *Res;
}

const ConstantRange &LHSRange = *LHSRes;
const ConstantRange &RHSRange = *RHSRes;
return ValueLatticeElement::getRange(OpFn(LHSRange, RHSRange));
Expand Down
92 changes: 92 additions & 0 deletions llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll
Original file line number Diff line number Diff line change
Expand Up @@ -630,3 +630,95 @@ define i64 @test_shl_nsw_at_use(i64 noundef %x) {
%res = select i1 %cmp, i64 %shr, i64 0
ret i64 %res
}

define i1 @test_icmp_mod(i64 noundef %x) {
; CHECK-LABEL: @test_icmp_mod(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
; CHECK-NEXT: ret i1 false
;
entry:
%rem = srem i64 %x, 86400
%cmp = icmp slt i64 %rem, 0
%cond = select i1 %cmp, i64 86400, i64 0
%add = add nsw i64 %cond, %rem
%cmp1 = icmp ugt i64 %add, 86399
ret i1 %cmp1
}

define i1 @test_icmp_mod_commuted1(i64 noundef %x) {
; CHECK-LABEL: @test_icmp_mod_commuted1(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[REM]], [[COND]]
; CHECK-NEXT: ret i1 false
;
entry:
%rem = srem i64 %x, 86400
%cmp = icmp slt i64 %rem, 0
%cond = select i1 %cmp, i64 86400, i64 0
%add = add nsw i64 %rem, %cond
%cmp1 = icmp ugt i64 %add, 86399
ret i1 %cmp1
}

define i1 @test_icmp_mod_commuted2(i64 noundef %x) {
; CHECK-LABEL: @test_icmp_mod_commuted2(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
; CHECK-NEXT: [[CMP:%.*]] = icmp sgt i64 [[REM]], -1
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 0, i64 86400
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
; CHECK-NEXT: ret i1 false
;
entry:
%rem = srem i64 %x, 86400
%cmp = icmp sgt i64 %rem, -1
%cond = select i1 %cmp, i64 0, i64 86400
%add = add nsw i64 %cond, %rem
%cmp1 = icmp ugt i64 %add, 86399
ret i1 %cmp1
}

define i1 @test_icmp_mod_undef(i64 %x) {
; CHECK-LABEL: @test_icmp_mod_undef(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86400, i64 0
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp ugt i64 [[ADD]], 86399
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%rem = srem i64 %x, 86400
%cmp = icmp slt i64 %rem, 0
%cond = select i1 %cmp, i64 86400, i64 0
%add = add nsw i64 %cond, %rem
%cmp1 = icmp ugt i64 %add, 86399
ret i1 %cmp1
}

define i1 @test_icmp_mod_wrong_range(i64 noundef %x) {
; CHECK-LABEL: @test_icmp_mod_wrong_range(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[REM:%.*]] = srem i64 [[X:%.*]], 86400
; CHECK-NEXT: [[CMP:%.*]] = icmp slt i64 [[REM]], 0
; CHECK-NEXT: [[COND:%.*]] = select i1 [[CMP]], i64 86401, i64 0
; CHECK-NEXT: [[ADD:%.*]] = add nsw i64 [[COND]], [[REM]]
; CHECK-NEXT: [[CMP1:%.*]] = icmp samesign ugt i64 [[ADD]], 86399
; CHECK-NEXT: ret i1 [[CMP1]]
;
entry:
%rem = srem i64 %x, 86400
%cmp = icmp slt i64 %rem, 0
%cond = select i1 %cmp, i64 86401, i64 0
%add = add nsw i64 %cond, %rem
%cmp1 = icmp ugt i64 %add, 86399
ret i1 %cmp1
}
Loading