[llvm] c871d0c - [LV] Avoid collapsing VF to zero when a loop has no memory ops or reductions (#216266)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 20 01:49:41 PDT 2026


Author: Pengcheng Wang
Date: 2026-08-20T08:49:35Z
New Revision: c871d0c17c0680ea584f8aba5f51661dbd8d77df

URL: https://github.com/llvm/llvm-project/commit/c871d0c17c0680ea584f8aba5f51661dbd8d77df
DIFF: https://github.com/llvm/llvm-project/commit/c871d0c17c0680ea584f8aba5f51661dbd8d77df.diff

LOG: [LV] Avoid collapsing VF to zero when a loop has no memory ops or reductions (#216266)

getSmallestAndWidestTypes() initializes MinWidth to the -1U sentinel and
only updates it from loads, stores, and reduction recurrences. A loop with
none of these (for example an early-exit search loop) therefore reports
SmallestType as the sentinel value.

The max-bandwidth VF computation in getMaximizedVFForTarget() divides
the widest register width by SmallestType. With the sentinel this underflows
to zero, so MaxVF collapses to an empty ElementCount and vectorization
is disabled. As a result -vectorizer-maximize-bandwidth, which is meant to
allow a larger VF, can instead turn a loop that vectorizes by default into a
scalar loop.

Restore the SmallestType <= WidestType invariant by falling back to MaxWidth
when no element type was observed. This keeps the default path unchanged
and makes the max-bandwidth path pick the same VF for such loops.

Assisted-by: TRAE CLI (Opus 4.8)

Added: 
    llvm/test/Transforms/LoopVectorize/RISCV/smallest-and-widest-types.ll

Modified: 
    llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.cpp b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.cpp
index 5f0717fbd5a03..cbe2f63f96005 100644
--- a/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.cpp
+++ b/llvm/lib/Transforms/Vectorize/LoopVectorizationPlanner.cpp
@@ -531,6 +531,15 @@ VFSelectionContext::getSmallestAndWidestTypes() const {
           MaxWidth, DL.getTypeSizeInBits(T->getScalarType()).getFixedValue());
     }
   }
+
+  // If the loop has no loads/stores or reductions (e.g. a search loop with an
+  // early exit), MinWidth is never updated and is left at its sentinel value.
+  // Fall back to MaxWidth to keep the SmallestType <= WidestType invariant, so
+  // callers such as the max-bandwidth VF computation don't divide by the
+  // sentinel and collapse the VF to zero.
+  if (MinWidth == -1U)
+    MinWidth = MaxWidth;
+
   return {MinWidth, MaxWidth};
 }
 

diff  --git a/llvm/test/Transforms/LoopVectorize/RISCV/smallest-and-widest-types.ll b/llvm/test/Transforms/LoopVectorize/RISCV/smallest-and-widest-types.ll
new file mode 100644
index 0000000000000..2c0f4a051136d
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/RISCV/smallest-and-widest-types.ll
@@ -0,0 +1,41 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --filter "The Smallest and Widest types" --filter "Selecting VF" --filter "Vectorization is possible but not beneficial" --version 6
+; REQUIRES: asserts
+; RUN: opt -passes=loop-vectorize -mtriple riscv64 -mattr=+v \
+; RUN:   -debug-only=loop-vectorize -disable-output -S < %s 2>&1 | FileCheck %s
+; RUN: opt -passes=loop-vectorize -mtriple riscv64 -mattr=+v \
+; RUN:   -vectorizer-maximize-bandwidth \
+; RUN:   -debug-only=loop-vectorize -disable-output -S < %s 2>&1 | FileCheck %s
+
+; This is an early-exit search loop with no loads/stores and no reductions, so
+; getSmallestAndWidestTypes() cannot observe any element type. The smallest type
+; is reported as equal to the widest type instead of the -1U sentinel, so the
+; max-bandwidth VF computation does not divide the register width by the
+; sentinel and collapse the VF to zero. With and without
+; -vectorizer-maximize-bandwidth we pick the same VF.
+
+define i32 @find_first_ge(i32 %n, i32 %target) {
+; CHECK-LABEL: 'find_first_ge'
+; CHECK:  LV: The Smallest and Widest types: 8 / 8 bits.
+; CHECK:  LV: Selecting VF: vscale x 16.
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i32 [ %iv.next, %loop.latch ], [ 0, %entry ]
+  %shl = shl i32 %iv, 1
+  %add = add i32 %shl, 512
+  %cmp = icmp ult i32 %add, %target
+  br i1 %cmp, label %loop.latch, label %exit.early
+
+loop.latch:
+  %iv.next = add nuw i32 %iv, 1
+  %exitcond = icmp eq i32 %iv.next, %n
+  br i1 %exitcond, label %exit, label %loop.header
+
+exit.early:
+  ret i32 %iv
+
+exit:
+  ret i32 %n
+}


        


More information about the llvm-commits mailing list