[llvm] [SCEV][LSR] Don't infer nowrap flags for non-SCEVable binops (PR #215019)
via llvm-commits
llvm-commits at lists.llvm.org
Sat Aug 8 13:15:08 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Kacper Doga (varev-dev)
<details>
<summary>Changes</summary>
`getStrengthenedNoWrapFlagsFromBinOp()` unconditionally calls `getSCEV()` on the operands of the binop it is given, which asserts for types SCEV does not model.
SCEVExpander reaches it with such binop. When it reuses an existing value, it walk that value's operand DAG to collect instructions whose poison generating flags must be dropped, then tries to re-infer them. That walk follows the IR, which is not restricted to SCEVable types: in the test, `computeKnownBits()` sees through an `extractelement` and proves the truncated result is zero, so the SCEV `(-1 * %p)` maps back to a value computed from a vector `add nsw`. LSR then reuses it, drops the flags and asks SCEV to reinfer them for the vector op.
Return `std::nullopt` for Operations SCEV cannot model instead.
Fixes #<!-- -->114029
---
Full diff: https://github.com/llvm/llvm-project/pull/215019.diff
3 Files Affected:
- (modified) llvm/include/llvm/Analysis/ScalarEvolution.h (+2-1)
- (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+4)
- (added) llvm/test/Transforms/LoopStrengthReduce/reuse-value-with-vector-operand.ll (+47)
``````````diff
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 5c01da0855f66..379295753c650 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -714,7 +714,8 @@ class ScalarEvolution {
/// SCEV no-wrap flags, and deduce flag[s] that aren't known yet.
/// Does not mutate the original instruction. Returns std::nullopt if it could
/// not deduce more precise flags than the instruction already has, otherwise
- /// returns proven flags.
+ /// returns proven flags. \p OBO does not need to be of a type SCEV can model;
+ /// std::nullopt is returned for operations SCEV cannot reason about.
LLVM_ABI std::optional<SCEV::NoWrapFlags>
getStrengthenedNoWrapFlagsFromBinOp(const OverflowingBinaryOperator *OBO);
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 2862acfedb91d..74f359df80263 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2492,6 +2492,10 @@ bool ScalarEvolution::willNotOverflow(Instruction::BinaryOps BinOp, bool Signed,
std::optional<SCEV::NoWrapFlags>
ScalarEvolution::getStrengthenedNoWrapFlagsFromBinOp(
const OverflowingBinaryOperator *OBO) {
+ // SCEV cannot reason about types it does not model, e.g. vectors.
+ if (!isSCEVable(OBO->getType()))
+ return std::nullopt;
+
// It cannot be done any better.
if (OBO->hasNoUnsignedWrap() && OBO->hasNoSignedWrap())
return std::nullopt;
diff --git a/llvm/test/Transforms/LoopStrengthReduce/reuse-value-with-vector-operand.ll b/llvm/test/Transforms/LoopStrengthReduce/reuse-value-with-vector-operand.ll
new file mode 100644
index 0000000000000..5ee558ac1b4cc
--- /dev/null
+++ b/llvm/test/Transforms/LoopStrengthReduce/reuse-value-with-vector-operand.ll
@@ -0,0 +1,47 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-reduce -S %s | FileCheck %s
+
+; LSR reuses %sub, which is computed from a vector op. The nsw on %vadd is
+; dropped, and SCEV must not try to re-infer it.
+
+target datalayout = "n32"
+
+declare i32 @get()
+
+define void @test(i1 %c, ptr %q) {
+; CHECK-LABEL: define void @test(
+; CHECK-SAME: i1 [[C:%.*]], ptr [[Q:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[P:%.*]] = call i32 @get()
+; CHECK-NEXT: [[VADD:%.*]] = add <2 x i64> zeroinitializer, zeroinitializer
+; CHECK-NEXT: [[EE:%.*]] = extractelement <2 x i64> [[VADD]], i64 0
+; CHECK-NEXT: [[T:%.*]] = trunc i64 [[EE]] to i32
+; CHECK-NEXT: [[SUB:%.*]] = sub i32 [[T]], [[P]]
+; CHECK-NEXT: [[ADD:%.*]] = add i32 [[IV]], [[SUB]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[Q]], align 4
+; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT: br i1 [[C]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop
+
+loop:
+ %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+ %p = call i32 @get()
+ %vadd = add nsw <2 x i64> zeroinitializer, zeroinitializer
+ %ee = extractelement <2 x i64> %vadd, i64 0
+ %t = trunc i64 %ee to i32
+ %sub = sub i32 %t, %p
+ %add = add i32 %iv, %sub
+ store i32 %add, ptr %q
+ %iv.next = add i32 %iv, 1
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/215019
More information about the llvm-commits
mailing list