[llvm] [IVDescriptors] Check for null SE before dereference in getMinMaxRecurrence. (PR #195798)
Craig Topper via llvm-commits
llvm-commits at lists.llvm.org
Tue May 5 11:56:47 PDT 2026
https://github.com/topperc updated https://github.com/llvm/llvm-project/pull/195798
>From 84eff50295d8058699a26e125040ff1c182c03af Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Mon, 4 May 2026 23:23:51 -0700
Subject: [PATCH 1/2] [IVDescriptors] Check for null SE before deference in
getMinMaxRecurrence.
Some callers don't pass SCEV.
---
llvm/lib/Analysis/IVDescriptors.cpp | 2 +
llvm/unittests/Analysis/IVDescriptorsTest.cpp | 45 +++++++++++++++++++
2 files changed, 47 insertions(+)
diff --git a/llvm/lib/Analysis/IVDescriptors.cpp b/llvm/lib/Analysis/IVDescriptors.cpp
index 96d30fd1b1415..7549e14366d2c 100644
--- a/llvm/lib/Analysis/IVDescriptors.cpp
+++ b/llvm/lib/Analysis/IVDescriptors.cpp
@@ -393,6 +393,8 @@ static RecurrenceDescriptor getMinMaxRecurrence(PHINode *Phi, Loop *TheLoop,
StoreInst *IntermediateStore = nullptr;
const SCEV *StorePtrSCEV = nullptr;
for (StoreInst *SI : Stores) {
+ if (!SE)
+ return {};
const SCEV *Ptr = SE->getSCEV(SI->getPointerOperand());
if (!SE->isLoopInvariant(Ptr, TheLoop) ||
(StorePtrSCEV && StorePtrSCEV != Ptr))
diff --git a/llvm/unittests/Analysis/IVDescriptorsTest.cpp b/llvm/unittests/Analysis/IVDescriptorsTest.cpp
index 400e90d2e52bf..a56f9800fed76 100644
--- a/llvm/unittests/Analysis/IVDescriptorsTest.cpp
+++ b/llvm/unittests/Analysis/IVDescriptorsTest.cpp
@@ -338,3 +338,48 @@ TEST(IVDescriptorsTest, UnsupportedFindLastPhi) {
EXPECT_FALSE(IsRdxPhi);
});
}
+
+// Make sure isReductionPHI doesn't crash when SE is not passed to it.
+TEST(IVDescriptorsTest, InvariantStoreNoSCEV) {
+ // Parse the module.
+ LLVMContext Context;
+
+ std::unique_ptr<Module> M = parseIR(Context, R"(
+ define void @smax_with_invariant_store_user(ptr noalias %src, ptr %dst, i64 %n) {
+ entry:
+ br label %loop
+
+ loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %max = phi i32 [ 0, %entry ], [ %max.next, %loop ]
+ %gep.src = getelementptr inbounds i32, ptr %src, i64 %iv
+ %l = load i32, ptr %gep.src, align 4
+ %max.next = tail call i32 @llvm.smax.i32(i32 %max, i32 %l)
+ store i32 %max.next, ptr %dst, align 4
+ %iv.next = add i64 %iv, 1
+ %ec = icmp eq i64 %iv, %n
+ br i1 %ec, label %exit, label %loop
+
+ exit:
+ ret void
+ })");
+
+ runWithLoopInfoAndSE(*M, "smax_with_invariant_store_user",
+ [&](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+ Function::iterator FI = F.begin();
+ // First basic block is entry - skip it.
+ BasicBlock *Header = &*(++FI);
+ assert(Header->getName() == "loop");
+ Loop *L = LI.getLoopFor(Header);
+ EXPECT_NE(L, nullptr);
+ BasicBlock::iterator BBI = Header->begin();
+ ++BBI;
+ PHINode *Phi = dyn_cast<PHINode>(&*BBI);
+ EXPECT_NE(Phi, nullptr);
+ EXPECT_EQ(Phi->getName(), "max");
+ RecurrenceDescriptor Rdx;
+ bool IsRdxPhi =
+ RecurrenceDescriptor::isReductionPHI(Phi, L, Rdx);
+ EXPECT_FALSE(IsRdxPhi);
+ });
+}
>From 40e536f5d8a324e72c2f26ba83d43ac4bc513a3f Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Tue, 5 May 2026 11:56:25 -0700
Subject: [PATCH 2/2] fixup! Add loop interchange test
---
.../LoopInterchange/reduction-crash.ll | 60 +++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 llvm/test/Transforms/LoopInterchange/reduction-crash.ll
diff --git a/llvm/test/Transforms/LoopInterchange/reduction-crash.ll b/llvm/test/Transforms/LoopInterchange/reduction-crash.ll
new file mode 100644
index 0000000000000..fcf218a146e38
--- /dev/null
+++ b/llvm/test/Transforms/LoopInterchange/reduction-crash.ll
@@ -0,0 +1,60 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -passes=loop-interchange -S | FileCheck %s
+
+; This test used to crash due to a dereference of a null ScalarEvolution pointer
+; inside isReductionPHI.
+define void @reduction_smax_store(ptr %A) {
+; CHECK-LABEL: define void @reduction_smax_store(
+; CHECK-SAME: ptr [[A:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_I_HEADER:.*]]
+; CHECK: [[FOR_I_HEADER]]:
+; CHECK-NEXT: [[I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[I_INC:%.*]], %[[FOR_I_LATCH:.*]] ]
+; CHECK-NEXT: [[MAX_I:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[MAX_I_LCSSA:%.*]], %[[FOR_I_LATCH]] ]
+; CHECK-NEXT: br label %[[FOR_J:.*]]
+; CHECK: [[FOR_J]]:
+; CHECK-NEXT: [[J:%.*]] = phi i32 [ 0, %[[FOR_I_HEADER]] ], [ [[J_INC:%.*]], %[[FOR_J]] ]
+; CHECK-NEXT: [[MAX_J:%.*]] = phi i32 [ [[MAX_I]], %[[FOR_I_HEADER]] ], [ [[MAX_J_NEXT:%.*]], %[[FOR_J]] ]
+; CHECK-NEXT: [[IDX:%.*]] = getelementptr inbounds [2 x [2 x i32]], ptr [[A]], i32 0, i32 [[J]], i32 [[I]]
+; CHECK-NEXT: [[A:%.*]] = load i32, ptr [[IDX]], align 4
+; CHECK-NEXT: [[MAX_J_NEXT]] = tail call i32 @llvm.smax.i32(i32 [[MAX_J]], i32 [[A]])
+; CHECK-NEXT: store i32 [[MAX_J_NEXT]], ptr [[IDX]], align 4
+; CHECK-NEXT: [[J_INC]] = add i32 [[J]], 1
+; CHECK-NEXT: [[CMP_J:%.*]] = icmp slt i32 [[J_INC]], 2
+; CHECK-NEXT: br i1 [[CMP_J]], label %[[FOR_J]], label %[[FOR_I_LATCH]]
+; CHECK: [[FOR_I_LATCH]]:
+; CHECK-NEXT: [[MAX_I_LCSSA]] = phi i32 [ [[MAX_J_NEXT]], %[[FOR_J]] ]
+; CHECK-NEXT: [[I_INC]] = add i32 [[I]], 1
+; CHECK-NEXT: [[CMP_I:%.*]] = icmp slt i32 [[I_INC]], 2
+; CHECK-NEXT: br i1 [[CMP_I]], label %[[FOR_I_HEADER]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.i.header
+
+for.i.header:
+ %i = phi i32 [ 0, %entry ], [ %i.inc, %for.i.latch ]
+ %max.i = phi i32 [ 0, %entry ], [ %max.i.lcssa, %for.i.latch ]
+ br label %for.j
+
+for.j:
+ %j = phi i32 [ 0, %for.i.header ], [ %j.inc, %for.j ]
+ %max.j = phi i32 [ %max.i, %for.i.header ], [ %max.j.next, %for.j ]
+ %idx = getelementptr inbounds [2 x [2 x i32]], ptr %A, i32 0, i32 %j, i32 %i
+ %a = load i32, ptr %idx, align 4
+ %max.j.next = tail call i32 @llvm.smax.i32(i32 %max.j, i32 %a)
+ store i32 %max.j.next, ptr %idx
+ %j.inc = add i32 %j, 1
+ %cmp.j = icmp slt i32 %j.inc, 2
+ br i1 %cmp.j, label %for.j, label %for.i.latch
+
+for.i.latch:
+ %max.i.lcssa = phi i32 [ %max.j.next, %for.j ]
+ %i.inc = add i32 %i, 1
+ %cmp.i = icmp slt i32 %i.inc, 2
+ br i1 %cmp.i, label %for.i.header, label %exit
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list