diff --git a/llvm/lib/Analysis/LazyValueInfo.cpp b/llvm/lib/Analysis/LazyValueInfo.cpp index c9849b86b664c..349a0a1a2d3c4 100644 --- a/llvm/lib/Analysis/LazyValueInfo.cpp +++ b/llvm/lib/Analysis/LazyValueInfo.cpp @@ -924,18 +924,65 @@ LazyValueInfoImpl::solveBlockValueBinaryOpImpl( Instruction *I, BasicBlock *BB, std::function OpFn) { + Value *LHS = I->getOperand(0); + Value *RHS = I->getOperand(1); + + auto ThreadBinOpOverSelect = + [&](Value *X, const ConstantRange &CRX, SelectInst *Y, + bool XIsLHS) -> std::optional { + Value *Cond = Y->getCondition(); + // Only handle selects with constant values. + Constant *TrueC = dyn_cast(Y->getTrueValue()); + if (!TrueC) + return std::nullopt; + Constant *FalseC = dyn_cast(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 LHSRes = getRangeFor(I->getOperand(0), I, BB); + std::optional LHSRes = getRangeFor(LHS, I, BB); if (!LHSRes) return std::nullopt; - std::optional RHSRes = getRangeFor(I->getOperand(1), I, BB); + // Try to thread binop over rhs select + if (auto *SI = dyn_cast(RHS)) { + if (auto Res = ThreadBinOpOverSelect(LHS, *LHSRes, SI, /*XIsLHS=*/true)) + return *Res; + } + + std::optional RHSRes = getRangeFor(RHS, I, BB); if (!RHSRes) return std::nullopt; + // Try to thread binop over lhs select + if (auto *SI = dyn_cast(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)); diff --git a/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll b/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll index 3af4c70a5621c..cca648bb6ea23 100644 --- a/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll +++ b/llvm/test/Transforms/CorrelatedValuePropagation/cond-at-use.ll @@ -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 +}