Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
3 changes: 3 additions & 0 deletions llvm/include/llvm/Analysis/ScalarEvolution.h
Original file line number Diff line number Diff line change
Expand Up @@ -2187,6 +2187,9 @@ class ScalarEvolution {
bool isGuaranteedToTransferExecutionTo(const Instruction *A,
const Instruction *B);

/// Returns true if \p Op is guaranteed not to cause immediate UB.
bool isGuaranteedNotToCauseUB(const SCEV *Op);

/// Returns true if \p Op is guaranteed to not be poison.
static bool isGuaranteedNotToBePoison(const SCEV *Op);

Expand Down
47 changes: 26 additions & 21 deletions llvm/lib/Analysis/ScalarEvolution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4307,15 +4307,7 @@ ScalarEvolution::getSequentialMinMaxExpr(SCEVTypes Kind,
}

for (unsigned i = 1, e = Ops.size(); i != e; ++i) {
bool MayBeUB = SCEVExprContains(Ops[i], [this](const SCEV *S) {
auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
// The UDiv may be UB if the divisor is poison or zero. Unless the divisor
// is a non-zero constant, we have to assume the UDiv may be UB.
return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
!isGuaranteedNotToBePoison(UDiv->getOperand(1)));
});

if (MayBeUB)
if (!isGuaranteedNotToCauseUB(Ops[i]))
continue;
// We can replace %x umin_seq %y with %x umin %y if either:
// * %y being poison implies %x is also poison.
Expand Down Expand Up @@ -5933,18 +5925,21 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
// We can generalize this saying that i is the shifted value of BEValue
// by one iteration:
// PHI(f(0), f({1,+,1})) --> f({0,+,1})
const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
if (Shifted != getCouldNotCompute() &&
Start != getCouldNotCompute()) {
const SCEV *StartVal = getSCEV(StartValueV);
if (Start == StartVal) {
// Okay, for the entire analysis of this edge we assumed the PHI
// to be symbolic. We now need to go back and purge all of the
// entries for the scalars that use the symbolic expression.
forgetMemoizedResults(SymbolicName);
insertValueToMap(PN, Shifted);
return Shifted;

// Do not allow refinement in rewriting of BEValue.
if (isGuaranteedNotToCauseUB(BEValue)) {
const SCEV *Shifted = SCEVShiftRewriter::rewrite(BEValue, L, *this);
const SCEV *Start = SCEVInitRewriter::rewrite(Shifted, L, *this, false);
if (Shifted != getCouldNotCompute() && Start != getCouldNotCompute()) {
const SCEV *StartVal = getSCEV(StartValueV);
if (Start == StartVal) {
// Okay, for the entire analysis of this edge we assumed the PHI
// to be symbolic. We now need to go back and purge all of the
// entries for the scalars that use the symbolic expression.
forgetMemoizedResults(SymbolicName);
insertValueToMap(PN, Shifted);
return Shifted;
}
}
}
}
Expand Down Expand Up @@ -7322,6 +7317,16 @@ bool ScalarEvolution::isGuaranteedNotToBePoison(const SCEV *Op) {
return PC.MaybePoison.empty();
}

bool ScalarEvolution::isGuaranteedNotToCauseUB(const SCEV *Op) {
return !SCEVExprContains(Op, [this](const SCEV *S) {
auto *UDiv = dyn_cast<SCEVUDivExpr>(S);
// The UDiv may be UB if the divisor is poison or zero. Unless the divisor
// is a non-zero constant, we have to assume the UDiv may be UB.
return UDiv && (!isKnownNonZero(UDiv->getOperand(1)) ||
!isGuaranteedNotToBePoison(UDiv->getOperand(1)));
});
}

bool ScalarEvolution::isSCEVExprNeverPoison(const Instruction *I) {
// Only proceed if we can prove that I does not yield poison.
if (!programUndefinedIfPoison(I))
Expand Down
2 changes: 1 addition & 1 deletion llvm/test/Analysis/ScalarEvolution/fold.ll
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ define i64 @test10(i64 %a, i64 %b) {
ret i64 %t2
}

define i64 @test11(i64 %a) {
define i64 @test11(i64 noundef range(i64 1, 0) %a) {
; CHECK-LABEL: 'test11'
; CHECK-NEXT: Classifying expressions for: @test11
; CHECK-NEXT: %t0 = udiv i64 0, %a
Expand Down
53 changes: 53 additions & 0 deletions llvm/test/Analysis/ScalarEvolution/pr117133.ll
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be great to construct a test case where poison is problematic.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -disable-output "-passes=print<scalar-evolution>" < %s 2>&1 | FileCheck %s

define i32 @widget() {
; CHECK-LABEL: 'widget'
; CHECK-NEXT: Classifying expressions for: @widget
; CHECK-NEXT: %phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
; CHECK-NEXT: --> %phi U: [0,1) S: [0,1) Exits: <<Unknown>> LoopDispositions: { %b1: Variant }
; CHECK-NEXT: %phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
; CHECK-NEXT: --> {1,+,1}<nuw><nsw><%b1> U: [1,2) S: [1,2) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
; CHECK-NEXT: %udiv = udiv i32 10, %phi2
; CHECK-NEXT: --> (10 /u {1,+,1}<nuw><nsw><%b1>) U: [10,11) S: [10,11) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
; CHECK-NEXT: %urem = urem i32 %udiv, 10
; CHECK-NEXT: --> ((-10 * ((10 /u {1,+,1}<nuw><nsw><%b1>) /u 10))<nuw><nsw> + (10 /u {1,+,1}<nuw><nsw><%b1>)) U: [0,1) S: [0,1) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
; CHECK-NEXT: %udiv6 = udiv i32 %phi2, 0
; CHECK-NEXT: --> ({1,+,1}<nuw><nsw><%b1> /u 0) U: empty-set S: empty-set Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
; CHECK-NEXT: %add = add i32 %phi2, 1
; CHECK-NEXT: --> {2,+,1}<nuw><nsw><%b1> U: [2,3) S: [2,3) Exits: <<Unknown>> LoopDispositions: { %b1: Computable }
; CHECK-NEXT: Determining loop execution counts for: @widget
; CHECK-NEXT: Loop %b1: <multiple exits> Unpredictable backedge-taken count.
; CHECK-NEXT: exit count for b1: ***COULDNOTCOMPUTE***
; CHECK-NEXT: exit count for b3: i32 0
; CHECK-NEXT: Loop %b1: constant max backedge-taken count is i32 0
; CHECK-NEXT: Loop %b1: symbolic max backedge-taken count is i32 0
; CHECK-NEXT: symbolic max exit count for b1: ***COULDNOTCOMPUTE***
; CHECK-NEXT: symbolic max exit count for b3: i32 0
;
b:
br label %b1

b1: ; preds = %b5, %b
%phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
%phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
%icmp = icmp eq i32 %phi, 0
br i1 %icmp, label %b3, label %b8

b3: ; preds = %b1
%udiv = udiv i32 10, %phi2
%urem = urem i32 %udiv, 10
%icmp4 = icmp eq i32 %urem, 0
br i1 %icmp4, label %b7, label %b5

b5: ; preds = %b3
%udiv6 = udiv i32 %phi2, 0
%add = add i32 %phi2, 1
br label %b1

b7: ; preds = %b3
ret i32 5

b8: ; preds = %b1
ret i32 7
}
4 changes: 2 additions & 2 deletions llvm/test/Analysis/ScalarEvolution/udiv-of-x-xsmaxone-fold.ll
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py
; RUN: opt -disable-output -passes="print<scalar-evolution>" < %s 2>&1 | FileCheck %s

define i32 @test_expr_with_constant_1(i32 %x) {
define i32 @test_expr_with_constant_1(i32 noundef range(i32 1, 0) %x) {
; CHECK-LABEL: 'test_expr_with_constant_1'
; CHECK-NEXT: Classifying expressions for: @test_expr_with_constant_1
; CHECK-NEXT: %smax = tail call i32 @llvm.smax.i32(i32 %x, i32 1)
Expand All @@ -20,7 +20,7 @@ entry:
}

; Non-1 constant: (-2 + (2 smax %x)) /u %x
define i32 @test_expr_with_constant_2(i32 %x) {
define i32 @test_expr_with_constant_2(i32 noundef range(i32 1, 0) %x) {
; CHECK-LABEL: 'test_expr_with_constant_2'
; CHECK-NEXT: Classifying expressions for: @test_expr_with_constant_2
; CHECK-NEXT: %smax = tail call i32 @llvm.smax.i32(i32 %x, i32 2)
Expand Down
45 changes: 45 additions & 0 deletions llvm/test/Transforms/IndVarSimplify/pr117133.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
; RUN: opt -S -passes=indvars < %s | FileCheck %s


define i32 @widget() {
; CHECK-LABEL: define i32 @widget() {
; CHECK-NEXT: [[B:.*:]]
; CHECK-NEXT: br label %[[B1:.*]]
; CHECK: [[B1]]:
; CHECK-NEXT: br i1 true, label %[[B3:.*]], label %[[B8:.*]]
; CHECK: [[B3]]:
; CHECK-NEXT: br i1 true, label %[[B7:.*]], label %[[B5:.*]]
; CHECK: [[B5]]:
; CHECK-NEXT: br label %[[B1]]
; CHECK: [[B7]]:
; CHECK-NEXT: ret i32 5
; CHECK: [[B8]]:
; CHECK-NEXT: ret i32 7
;
b:
br label %b1

b1:
%phi = phi i32 [ 0, %b ], [ %udiv6, %b5 ]
%phi2 = phi i32 [ 1, %b ], [ %add, %b5 ]
%icmp = icmp eq i32 %phi, 0
br i1 %icmp, label %b3, label %b8

b3:
%udiv = udiv i32 10, %phi2
%urem = urem i32 %udiv, 10
%icmp4 = icmp eq i32 %urem, 0
br i1 %icmp4, label %b7, label %b5

b5:
%udiv6 = udiv i32 %phi2, 0
%add = add i32 %phi2, 1
br label %b1

b7:
ret i32 5

b8:
ret i32 7
}
Loading