[llvm] [SCEVExpander] Don't expand a UDiv with a possibly-poison divisor (PR #202378)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Jun 8 09:05:58 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: lijinpei-amd
<details>
<summary>Changes</summary>
SCEVExpander::isSafeToExpand only check divisor isKnownNonZero, which
ignore the possibility of poison. For the following divisor:
```
%ct = call i32 @<!-- -->llvm.cttz.i32(i32 %x, i1 true)
%divisor = add i32 %ct, 1
...
%rem = urem i32 1, %divisor
```
The urem may be hoisted unsafely.
Fix by also check divisor isGuaranteedNotToBePoison.
Fixes https://github.com/llvm/llvm-project/issues/202028
---
Full diff: https://github.com/llvm/llvm-project/pull/202378.diff
3 Files Affected:
- (modified) llvm/include/llvm/Analysis/ScalarEvolution.h (+3-3)
- (modified) llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp (+2-1)
- (added) llvm/test/Transforms/IndVarSimplify/X86/exit-value-safe-udiv.ll (+44)
``````````diff
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 15b891b5b4da9..50af763614a31 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -1235,6 +1235,9 @@ class ScalarEvolution {
/// Test if the given expression is known to be non-zero.
LLVM_ABI bool isKnownNonZero(const SCEV *S);
+ /// Returns true if \p Op is guaranteed to not be poison.
+ LLVM_ABI static bool isGuaranteedNotToBePoison(const SCEV *Op);
+
/// Test if the given expression is known to be a power of 2. OrNegative
/// allows matching negative power of 2s, and OrZero allows matching 0.
LLVM_ABI bool isKnownToBeAPowerOfTwo(const SCEV *S, bool OrZero = false,
@@ -2427,9 +2430,6 @@ class ScalarEvolution {
/// 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);
-
/// Return true if the SCEV corresponding to \p I is never poison. Proving
/// this is more complex than proving that just \p I is never poison, since
/// SCEV commons expressions across control flow, and you can have cases
diff --git a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
index d747c7fb2d162..b2e45c3340b9f 100644
--- a/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
+++ b/llvm/lib/Transforms/Utils/ScalarEvolutionExpander.cpp
@@ -2449,7 +2449,8 @@ struct SCEVFindUnsafe {
bool follow(const SCEV *S) {
if (const SCEVUDivExpr *D = dyn_cast<SCEVUDivExpr>(S)) {
- if (!SE.isKnownNonZero(D->getRHS())) {
+ if (!SE.isKnownNonZero(D->getRHS()) ||
+ !SE.isGuaranteedNotToBePoison(D->getRHS())) {
IsUnsafe = true;
return false;
}
diff --git a/llvm/test/Transforms/IndVarSimplify/X86/exit-value-safe-udiv.ll b/llvm/test/Transforms/IndVarSimplify/X86/exit-value-safe-udiv.ll
new file mode 100644
index 0000000000000..453d93cea2708
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/X86/exit-value-safe-udiv.ll
@@ -0,0 +1,44 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -S -passes=indvars < %s | FileCheck %s
+
+; Reduced from https://github.com/llvm/llvm-project/issues/202028.
+
+target triple = "x86_64-unknown-linux-gnu"
+
+declare i32 @llvm.cttz.i32(i32, i1)
+
+define i32 @speculated_exit_value_udiv(i32 %x, i1 %guard, i1 %backedge) {
+; CHECK-LABEL: @speculated_exit_value_udiv(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CT:%.*]] = call i32 @llvm.cttz.i32(i32 [[X:%.*]], i1 true)
+; CHECK-NEXT: [[D:%.*]] = add i32 [[CT]], 1
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: br i1 [[GUARD:%.*]], label [[EXIT_EARLY:%.*]], label [[BODY:%.*]]
+; CHECK: body:
+; CHECK-NEXT: [[REM:%.*]] = urem i32 1, [[D]]
+; CHECK-NEXT: br i1 [[BACKEDGE:%.*]], label [[LOOP]], label [[EXIT_LATE:%.*]]
+; CHECK: exit.early:
+; CHECK-NEXT: ret i32 0
+; CHECK: exit.late:
+; CHECK-NEXT: [[REM_LCSSA:%.*]] = phi i32 [ [[REM]], [[BODY]] ]
+; CHECK-NEXT: ret i32 [[REM_LCSSA]]
+;
+entry:
+ %ct = call i32 @llvm.cttz.i32(i32 %x, i1 true)
+ %d = add i32 %ct, 1
+ br label %loop
+
+loop:
+ br i1 %guard, label %exit.early, label %body
+
+body:
+ %rem = urem i32 1, %d
+ br i1 %backedge, label %loop, label %exit.late
+
+exit.early:
+ ret i32 0
+
+exit.late:
+ ret i32 %rem
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/202378
More information about the llvm-commits
mailing list