[llvm] [SCEV] Fix infinite recursion in getZeroExtendExpr via missing Depth propagation (PR #184958)

via llvm-commits llvm-commits at lists.llvm.org
Fri Mar 6 07:37:17 PST 2026


https://github.com/dean-mccoppin updated https://github.com/llvm/llvm-project/pull/184958

>From 23d93f38d38e837be049f5e3994fc87eb091b321 Mon Sep 17 00:00:00 2001
From: hshira6 <hshira6 at wgu.edu>
Date: Fri, 6 Mar 2026 01:50:24 -0500
Subject: [PATCH 1/3] [SCEV] Fix infinite recursion in getZeroExtendExpr via
 missing Depth propagation

getZeroExtendExprImpl called getAddExpr with a proper Depth argument, but
getAddExpr called back into getZeroExtendExpr without propagating Depth
(defaulting to 0), defeating the MaxCastDepth recursion guard and causing
a stack overflow when compiling loops with two interacting induction variables.

Fix by passing Depth through all getZeroExtendExpr call sites inside
getAddExpr and the missing sites inside getZeroExtendExprImpl itself
(2^K mul-trunc and umin/umax/umin_seq folding paths).

Fixes https://github.com/llvm/llvm-project/issues/184947
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 12 +++---
 .../Transforms/LoopStrengthReduce/pr184947.ll | 43 +++++++++++++++++++
 2 files changed, 49 insertions(+), 6 deletions(-)
 create mode 100644 llvm/test/Transforms/LoopStrengthReduce/pr184947.ll

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 083988f948069..732a32a7101e4 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -1879,8 +1879,8 @@ const SCEV *ScalarEvolution::getZeroExtendExprImpl(const SCEV *Op, Type *Ty,
           getTypeSizeInBits(SM->getOperand(1)->getType()) - C->logBase2();
       Type *NewTruncTy = IntegerType::get(getContext(), NewTruncBits);
       return getMulExpr(
-          getZeroExtendExpr(SM->getOperand(0), Ty),
-          getZeroExtendExpr(getTruncateExpr(TruncRHS, NewTruncTy), Ty),
+          getZeroExtendExpr(SM->getOperand(0), Ty, Depth + 1),
+          getZeroExtendExpr(getTruncateExpr(TruncRHS, NewTruncTy), Ty, Depth + 1),
           SCEV::FlagNUW, Depth + 1);
     }
   }
@@ -1891,7 +1891,7 @@ const SCEV *ScalarEvolution::getZeroExtendExprImpl(const SCEV *Op, Type *Ty,
     auto *MinMax = cast<SCEVMinMaxExpr>(Op);
     SmallVector<const SCEV *, 4> Operands;
     for (auto *Operand : MinMax->operands())
-      Operands.push_back(getZeroExtendExpr(Operand, Ty));
+      Operands.push_back(getZeroExtendExpr(Operand, Ty, Depth + 1));
     if (isa<SCEVUMinExpr>(MinMax))
       return getUMinExpr(Operands);
     return getUMaxExpr(Operands);
@@ -1902,7 +1902,7 @@ const SCEV *ScalarEvolution::getZeroExtendExprImpl(const SCEV *Op, Type *Ty,
     assert(isa<SCEVSequentialUMinExpr>(MinMax) && "Not supported!");
     SmallVector<const SCEV *, 4> Operands;
     for (auto *Operand : MinMax->operands())
-      Operands.push_back(getZeroExtendExpr(Operand, Ty));
+      Operands.push_back(getZeroExtendExpr(Operand, Ty, Depth + 1));
     return getUMinExpr(Operands, /*Sequential*/ true);
   }
 
@@ -2719,11 +2719,11 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<const SCEV *> &Ops,
     if (match(B, m_scev_ZExt(m_scev_Add(InnerAdd)))) {
       const SCEV *NarrowA = getTruncateExpr(A, InnerAdd->getType());
       if (NarrowA == getNegativeSCEV(InnerAdd->getOperand(0)) &&
-          getZeroExtendExpr(NarrowA, B->getType()) == A &&
+          getZeroExtendExpr(NarrowA, B->getType(), Depth) == A &&
           hasFlags(StrengthenNoWrapFlags(this, scAddExpr, {NarrowA, InnerAdd},
                                          SCEV::FlagAnyWrap),
                    SCEV::FlagNUW)) {
-        return getZeroExtendExpr(getAddExpr(NarrowA, InnerAdd), B->getType());
+        return getZeroExtendExpr(getAddExpr(NarrowA, InnerAdd, SCEV::FlagAnyWrap, Depth + 1), B->getType(), Depth);
       }
     }
   }
diff --git a/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll b/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll
new file mode 100644
index 0000000000000..10d5bb8ac6289
--- /dev/null
+++ b/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll
@@ -0,0 +1,43 @@
+; RUN: opt -S -passes=loop-reduce < %s | FileCheck %s
+;
+; Make sure loop-reduce doesn't crash with infinite recursion in
+; getZeroExtendExpr via getAddExpr calling getZeroExtendExpr without
+; propagating the depth argument.
+; See https://github.com/llvm/llvm-project/issues/184947
+
+; Corresponds to:
+;   a() {
+;     int b = 0, c = 0;
+;     for (;; a) {
+;       c++;
+;       if (c <= 14) continue;
+;       b++;
+;       if (b <= 45) continue;
+;       return;
+;     }
+;   }
+
+; CHECK-LABEL: @a(
+define void @a() {
+entry:
+  br label %for.body
+
+for.body:
+  %b.0 = phi i32 [ 0, %entry ], [ %b.next, %for.latch ]
+  %c.0 = phi i32 [ 0, %entry ], [ %c.inc, %for.latch ]
+  %c.inc = add i32 %c.0, 1
+  %cmp.c = icmp sle i32 %c.inc, 14
+  br i1 %cmp.c, label %for.latch, label %b.inc.blk
+
+b.inc.blk:
+  %b.inc = add i32 %b.0, 1
+  %cmp.b = icmp sle i32 %b.inc, 45
+  br i1 %cmp.b, label %for.latch, label %return
+
+for.latch:
+  %b.next = phi i32 [ %b.0, %for.body ], [ %b.inc, %b.inc.blk ]
+  br label %for.body
+
+return:
+  ret void
+}

>From a000e3a85dbfe8eaa530788de7f6562f532bf76d Mon Sep 17 00:00:00 2001
From: hshira6 <hshira6 at wgu.edu>
Date: Fri, 6 Mar 2026 09:30:34 -0500
Subject: [PATCH 2/3] [SCEV] Use Depth+1 consistently in getAddExpr ->
 getZeroExtendExpr calls

Per review feedback, use Depth+1 (not Depth) at the two getZeroExtendExpr
call sites in the A + zext(-A + B) folding path in getAddExpr, consistent
with every other recursive call site in this file.
---
 llvm/lib/Analysis/ScalarEvolution.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 732a32a7101e4..71a1cb4301c73 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -2719,11 +2719,11 @@ const SCEV *ScalarEvolution::getAddExpr(SmallVectorImpl<const SCEV *> &Ops,
     if (match(B, m_scev_ZExt(m_scev_Add(InnerAdd)))) {
       const SCEV *NarrowA = getTruncateExpr(A, InnerAdd->getType());
       if (NarrowA == getNegativeSCEV(InnerAdd->getOperand(0)) &&
-          getZeroExtendExpr(NarrowA, B->getType(), Depth) == A &&
+          getZeroExtendExpr(NarrowA, B->getType(), Depth + 1) == A &&
           hasFlags(StrengthenNoWrapFlags(this, scAddExpr, {NarrowA, InnerAdd},
                                          SCEV::FlagAnyWrap),
                    SCEV::FlagNUW)) {
-        return getZeroExtendExpr(getAddExpr(NarrowA, InnerAdd, SCEV::FlagAnyWrap, Depth + 1), B->getType(), Depth);
+        return getZeroExtendExpr(getAddExpr(NarrowA, InnerAdd, SCEV::FlagAnyWrap, Depth + 1), B->getType(), Depth + 1);
       }
     }
   }

>From 2023a0c182116c9e87c5b154ded962d0dac1ad9e Mon Sep 17 00:00:00 2001
From: hshira6 <hshira6 at wgu.edu>
Date: Fri, 6 Mar 2026 10:34:15 -0500
Subject: [PATCH 3/3] [SCEV] Replace pr184947 test with IR that actually
 triggers the crash

The original hand-written IR didn't reproduce the bug. Use IR from
clang -O3 on the reproducer, where full loop unrolling creates 46
chained mini-loops that exercise the buggy SCEV code path.
---
 .../Transforms/LoopStrengthReduce/pr184947.ll | 261 ++++++++++++++++--
 1 file changed, 238 insertions(+), 23 deletions(-)

diff --git a/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll b/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll
index 10d5bb8ac6289..4a839410f8d39 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll
+++ b/llvm/test/Transforms/LoopStrengthReduce/pr184947.ll
@@ -4,8 +4,8 @@
 ; getZeroExtendExpr via getAddExpr calling getZeroExtendExpr without
 ; propagating the depth argument.
 ; See https://github.com/llvm/llvm-project/issues/184947
-
-; Corresponds to:
+;
+; IR produced by clang -O3 -emit-llvm -S from:
 ;   a() {
 ;     int b = 0, c = 0;
 ;     for (;; a) {
@@ -16,28 +16,243 @@
 ;       return;
 ;     }
 ;   }
+; The -O3 pipeline unrolls the outer loop, producing 46 chained mini-loops
+; whose chain of AddRecs triggered the infinite recursion in SCEV.
 
 ; CHECK-LABEL: @a(
-define void @a() {
-entry:
-  br label %for.body
-
-for.body:
-  %b.0 = phi i32 [ 0, %entry ], [ %b.next, %for.latch ]
-  %c.0 = phi i32 [ 0, %entry ], [ %c.inc, %for.latch ]
-  %c.inc = add i32 %c.0, 1
-  %cmp.c = icmp sle i32 %c.inc, 14
-  br i1 %cmp.c, label %for.latch, label %b.inc.blk
-
-b.inc.blk:
-  %b.inc = add i32 %b.0, 1
-  %cmp.b = icmp sle i32 %b.inc, 45
-  br i1 %cmp.b, label %for.latch, label %return
-
-for.latch:
-  %b.next = phi i32 [ %b.0, %for.body ], [ %b.inc, %b.inc.blk ]
-  br label %for.body
 
-return:
-  ret void
+define dso_local i32 @a() local_unnamed_addr {
+  br label %1
+1:                                                ; preds = %0, %1
+  %2 = phi i32 [ %3, %1 ], [ 0, %0 ]
+  %3 = add nuw nsw i32 %2, 1
+  %4 = icmp samesign ult i32 %2, 14
+  br i1 %4, label %1, label %5
+5:                                                ; preds = %1, %5
+  %6 = phi i32 [ %7, %5 ], [ %3, %1 ]
+  %7 = add nuw nsw i32 %6, 1
+  %8 = icmp samesign ult i32 %6, 14
+  br i1 %8, label %5, label %9
+9:                                                ; preds = %5, %9
+  %10 = phi i32 [ %11, %9 ], [ %7, %5 ]
+  %11 = add nuw nsw i32 %10, 1
+  %12 = icmp samesign ult i32 %10, 14
+  br i1 %12, label %9, label %13
+13:                                               ; preds = %9, %13
+  %14 = phi i32 [ %15, %13 ], [ %11, %9 ]
+  %15 = add nuw nsw i32 %14, 1
+  %16 = icmp samesign ult i32 %14, 14
+  br i1 %16, label %13, label %17
+17:                                               ; preds = %13, %17
+  %18 = phi i32 [ %19, %17 ], [ %15, %13 ]
+  %19 = add nuw nsw i32 %18, 1
+  %20 = icmp samesign ult i32 %18, 14
+  br i1 %20, label %17, label %21
+21:                                               ; preds = %17, %21
+  %22 = phi i32 [ %23, %21 ], [ %19, %17 ]
+  %23 = add nuw nsw i32 %22, 1
+  %24 = icmp samesign ult i32 %22, 14
+  br i1 %24, label %21, label %25
+25:                                               ; preds = %21, %25
+  %26 = phi i32 [ %27, %25 ], [ %23, %21 ]
+  %27 = add nuw nsw i32 %26, 1
+  %28 = icmp samesign ult i32 %26, 14
+  br i1 %28, label %25, label %29
+29:                                               ; preds = %25, %29
+  %30 = phi i32 [ %31, %29 ], [ %27, %25 ]
+  %31 = add nuw nsw i32 %30, 1
+  %32 = icmp samesign ult i32 %30, 14
+  br i1 %32, label %29, label %33
+33:                                               ; preds = %29, %33
+  %34 = phi i32 [ %35, %33 ], [ %31, %29 ]
+  %35 = add nuw nsw i32 %34, 1
+  %36 = icmp samesign ult i32 %34, 14
+  br i1 %36, label %33, label %37
+37:                                               ; preds = %33, %37
+  %38 = phi i32 [ %39, %37 ], [ %35, %33 ]
+  %39 = add nuw nsw i32 %38, 1
+  %40 = icmp samesign ult i32 %38, 14
+  br i1 %40, label %37, label %41
+41:                                               ; preds = %37, %41
+  %42 = phi i32 [ %43, %41 ], [ %39, %37 ]
+  %43 = add nuw nsw i32 %42, 1
+  %44 = icmp samesign ult i32 %42, 14
+  br i1 %44, label %41, label %45
+45:                                               ; preds = %41, %45
+  %46 = phi i32 [ %47, %45 ], [ %43, %41 ]
+  %47 = add nuw nsw i32 %46, 1
+  %48 = icmp samesign ult i32 %46, 14
+  br i1 %48, label %45, label %49
+49:                                               ; preds = %45, %49
+  %50 = phi i32 [ %51, %49 ], [ %47, %45 ]
+  %51 = add nuw nsw i32 %50, 1
+  %52 = icmp samesign ult i32 %50, 14
+  br i1 %52, label %49, label %53
+53:                                               ; preds = %49, %53
+  %54 = phi i32 [ %55, %53 ], [ %51, %49 ]
+  %55 = add nuw nsw i32 %54, 1
+  %56 = icmp samesign ult i32 %54, 14
+  br i1 %56, label %53, label %57
+57:                                               ; preds = %53, %57
+  %58 = phi i32 [ %59, %57 ], [ %55, %53 ]
+  %59 = add nuw nsw i32 %58, 1
+  %60 = icmp samesign ult i32 %58, 14
+  br i1 %60, label %57, label %61
+61:                                               ; preds = %57, %61
+  %62 = phi i32 [ %63, %61 ], [ %59, %57 ]
+  %63 = add nuw nsw i32 %62, 1
+  %64 = icmp samesign ult i32 %62, 14
+  br i1 %64, label %61, label %65
+65:                                               ; preds = %61, %65
+  %66 = phi i32 [ %67, %65 ], [ %63, %61 ]
+  %67 = add nuw nsw i32 %66, 1
+  %68 = icmp samesign ult i32 %66, 14
+  br i1 %68, label %65, label %69
+69:                                               ; preds = %65, %69
+  %70 = phi i32 [ %71, %69 ], [ %67, %65 ]
+  %71 = add nuw nsw i32 %70, 1
+  %72 = icmp samesign ult i32 %70, 14
+  br i1 %72, label %69, label %73
+73:                                               ; preds = %69, %73
+  %74 = phi i32 [ %75, %73 ], [ %71, %69 ]
+  %75 = add nuw nsw i32 %74, 1
+  %76 = icmp samesign ult i32 %74, 14
+  br i1 %76, label %73, label %77
+77:                                               ; preds = %73, %77
+  %78 = phi i32 [ %79, %77 ], [ %75, %73 ]
+  %79 = add nuw nsw i32 %78, 1
+  %80 = icmp samesign ult i32 %78, 14
+  br i1 %80, label %77, label %81
+81:                                               ; preds = %77, %81
+  %82 = phi i32 [ %83, %81 ], [ %79, %77 ]
+  %83 = add nuw nsw i32 %82, 1
+  %84 = icmp samesign ult i32 %82, 14
+  br i1 %84, label %81, label %85
+85:                                               ; preds = %81, %85
+  %86 = phi i32 [ %87, %85 ], [ %83, %81 ]
+  %87 = add nuw nsw i32 %86, 1
+  %88 = icmp samesign ult i32 %86, 14
+  br i1 %88, label %85, label %89
+89:                                               ; preds = %85, %89
+  %90 = phi i32 [ %91, %89 ], [ %87, %85 ]
+  %91 = add nuw nsw i32 %90, 1
+  %92 = icmp samesign ult i32 %90, 14
+  br i1 %92, label %89, label %93
+93:                                               ; preds = %89, %93
+  %94 = phi i32 [ %95, %93 ], [ %91, %89 ]
+  %95 = add nuw nsw i32 %94, 1
+  %96 = icmp samesign ult i32 %94, 14
+  br i1 %96, label %93, label %97
+97:                                               ; preds = %93, %97
+  %98 = phi i32 [ %99, %97 ], [ %95, %93 ]
+  %99 = add nuw nsw i32 %98, 1
+  %100 = icmp samesign ult i32 %98, 14
+  br i1 %100, label %97, label %101
+101:                                              ; preds = %97, %101
+  %102 = phi i32 [ %103, %101 ], [ %99, %97 ]
+  %103 = add nuw nsw i32 %102, 1
+  %104 = icmp samesign ult i32 %102, 14
+  br i1 %104, label %101, label %105
+105:                                              ; preds = %101, %105
+  %106 = phi i32 [ %107, %105 ], [ %103, %101 ]
+  %107 = add nuw nsw i32 %106, 1
+  %108 = icmp samesign ult i32 %106, 14
+  br i1 %108, label %105, label %109
+109:                                              ; preds = %105, %109
+  %110 = phi i32 [ %111, %109 ], [ %107, %105 ]
+  %111 = add nuw nsw i32 %110, 1
+  %112 = icmp samesign ult i32 %110, 14
+  br i1 %112, label %109, label %113
+113:                                              ; preds = %109, %113
+  %114 = phi i32 [ %115, %113 ], [ %111, %109 ]
+  %115 = add nuw nsw i32 %114, 1
+  %116 = icmp samesign ult i32 %114, 14
+  br i1 %116, label %113, label %117
+117:                                              ; preds = %113, %117
+  %118 = phi i32 [ %119, %117 ], [ %115, %113 ]
+  %119 = add nuw nsw i32 %118, 1
+  %120 = icmp samesign ult i32 %118, 14
+  br i1 %120, label %117, label %121
+121:                                              ; preds = %117, %121
+  %122 = phi i32 [ %123, %121 ], [ %119, %117 ]
+  %123 = add nuw nsw i32 %122, 1
+  %124 = icmp samesign ult i32 %122, 14
+  br i1 %124, label %121, label %125
+125:                                              ; preds = %121, %125
+  %126 = phi i32 [ %127, %125 ], [ %123, %121 ]
+  %127 = add nuw nsw i32 %126, 1
+  %128 = icmp samesign ult i32 %126, 14
+  br i1 %128, label %125, label %129
+129:                                              ; preds = %125, %129
+  %130 = phi i32 [ %131, %129 ], [ %127, %125 ]
+  %131 = add nuw nsw i32 %130, 1
+  %132 = icmp samesign ult i32 %130, 14
+  br i1 %132, label %129, label %133
+133:                                              ; preds = %129, %133
+  %134 = phi i32 [ %135, %133 ], [ %131, %129 ]
+  %135 = add nuw nsw i32 %134, 1
+  %136 = icmp samesign ult i32 %134, 14
+  br i1 %136, label %133, label %137
+137:                                              ; preds = %133, %137
+  %138 = phi i32 [ %139, %137 ], [ %135, %133 ]
+  %139 = add nuw nsw i32 %138, 1
+  %140 = icmp samesign ult i32 %138, 14
+  br i1 %140, label %137, label %141
+141:                                              ; preds = %137, %141
+  %142 = phi i32 [ %143, %141 ], [ %139, %137 ]
+  %143 = add nuw nsw i32 %142, 1
+  %144 = icmp samesign ult i32 %142, 14
+  br i1 %144, label %141, label %145
+145:                                              ; preds = %141, %145
+  %146 = phi i32 [ %147, %145 ], [ %143, %141 ]
+  %147 = add nuw nsw i32 %146, 1
+  %148 = icmp samesign ult i32 %146, 14
+  br i1 %148, label %145, label %149
+149:                                              ; preds = %145, %149
+  %150 = phi i32 [ %151, %149 ], [ %147, %145 ]
+  %151 = add nuw nsw i32 %150, 1
+  %152 = icmp samesign ult i32 %150, 14
+  br i1 %152, label %149, label %153
+153:                                              ; preds = %149, %153
+  %154 = phi i32 [ %155, %153 ], [ %151, %149 ]
+  %155 = add nuw nsw i32 %154, 1
+  %156 = icmp samesign ult i32 %154, 14
+  br i1 %156, label %153, label %157
+157:                                              ; preds = %153, %157
+  %158 = phi i32 [ %159, %157 ], [ %155, %153 ]
+  %159 = add nuw nsw i32 %158, 1
+  %160 = icmp samesign ult i32 %158, 14
+  br i1 %160, label %157, label %161
+161:                                              ; preds = %157, %161
+  %162 = phi i32 [ %163, %161 ], [ %159, %157 ]
+  %163 = add nuw nsw i32 %162, 1
+  %164 = icmp samesign ult i32 %162, 14
+  br i1 %164, label %161, label %165
+165:                                              ; preds = %161, %165
+  %166 = phi i32 [ %167, %165 ], [ %163, %161 ]
+  %167 = add nuw nsw i32 %166, 1
+  %168 = icmp samesign ult i32 %166, 14
+  br i1 %168, label %165, label %169
+169:                                              ; preds = %165, %169
+  %170 = phi i32 [ %171, %169 ], [ %167, %165 ]
+  %171 = add nuw nsw i32 %170, 1
+  %172 = icmp samesign ult i32 %170, 14
+  br i1 %172, label %169, label %173
+173:                                              ; preds = %169, %173
+  %174 = phi i32 [ %175, %173 ], [ %171, %169 ]
+  %175 = add nuw nsw i32 %174, 1
+  %176 = icmp samesign ult i32 %174, 14
+  br i1 %176, label %173, label %177
+177:                                              ; preds = %173, %177
+  %178 = phi i32 [ %179, %177 ], [ %175, %173 ]
+  %179 = add nuw nsw i32 %178, 1
+  %180 = icmp samesign ult i32 %178, 14
+  br i1 %180, label %177, label %181
+181:                                              ; preds = %177, %181
+  %182 = phi i32 [ %183, %181 ], [ %179, %177 ]
+  %183 = add nuw nsw i32 %182, 1
+  %184 = icmp samesign ult i32 %182, 14
+  br i1 %184, label %181, label %185
+185:                                              ; preds = %181
+  ret i32 undef
 }



More information about the llvm-commits mailing list