[llvm] [LV] Fix strict weak ordering violation in handleUncountableEarlyExits sort (PR #181462)

Brian Cain via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 13 22:13:20 PST 2026


https://github.com/androm3da created https://github.com/llvm/llvm-project/pull/181462

The sort comparator used VPDT.dominates() which returns true for dominates(A, A), violating the irreflexivity requirement of strict weak ordering. With _GLIBCXX_DEBUG enabled (LLVM_ENABLE_EXPENSIVE_CHECKS=ON), std::sort validates this property and aborts:

  Error: comparison doesn't meet irreflexive requirements, assert(!(a < a)).

Use properlyDominates() instead, which correctly returns false for equal inputs while preserving the intended dominance-based ordering.

This fixes a crash introduced by ede1a9626b89 ("[LV] Vectorize early exit loops with multiple exits.").

>From 3faf71c86f8836a1cc18fba29c478412edffe758 Mon Sep 17 00:00:00 2001
From: Brian Cain <brian.cain at oss.qualcomm.com>
Date: Fri, 13 Feb 2026 22:08:02 -0800
Subject: [PATCH] [LV] Fix strict weak ordering violation in
 handleUncountableEarlyExits sort

The sort comparator used VPDT.dominates() which returns true for
dominates(A, A), violating the irreflexivity requirement of strict weak
ordering. With _GLIBCXX_DEBUG enabled (LLVM_ENABLE_EXPENSIVE_CHECKS=ON),
std::sort validates this property and aborts:

  Error: comparison doesn't meet irreflexive requirements, assert(!(a < a)).

Use properlyDominates() instead, which correctly returns false for equal
inputs while preserving the intended dominance-based ordering.

This fixes a crash introduced by ede1a9626b89 ("[LV] Vectorize early
exit loops with multiple exits.").
---
 .../Transforms/Vectorize/VPlanTransforms.cpp  |  2 +-
 .../LoopVectorize/multiple-early-exits.ll     | 54 +++++++++++++++++++
 2 files changed, 55 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index da3afe7ce6d03..0d713a6dc4e47 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -4080,7 +4080,7 @@ void VPlanTransforms::handleUncountableEarlyExits(VPlan &Plan,
   assert(!Exits.empty() && "must have at least one early exit");
   // Sort exits by dominance to get the correct program order.
   llvm::sort(Exits, [&VPDT](const EarlyExitInfo &A, const EarlyExitInfo &B) {
-    return VPDT.dominates(A.EarlyExitingVPBB, B.EarlyExitingVPBB);
+    return VPDT.properlyDominates(A.EarlyExitingVPBB, B.EarlyExitingVPBB);
   });
 
   // Build the AnyOf condition for the latch terminator using logical OR
diff --git a/llvm/test/Transforms/LoopVectorize/multiple-early-exits.ll b/llvm/test/Transforms/LoopVectorize/multiple-early-exits.ll
index 4cabc711f5b7d..deb85f0dc5995 100644
--- a/llvm/test/Transforms/LoopVectorize/multiple-early-exits.ll
+++ b/llvm/test/Transforms/LoopVectorize/multiple-early-exits.ll
@@ -1533,3 +1533,57 @@ latch.exit:
 early.exit:
   ret i64 %iv
 }
+
+; Single early exit going to the same block as the latch exit. This exercises
+; the sort in handleUncountableEarlyExits with Exits.size()==1.
+define i64 @single_early_exit_same_exit_as_latch() {
+; CHECK-LABEL: define i64 @single_early_exit_same_exit_as_latch() {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    [[SRC:%.*]] = alloca [128 x i8], align 1
+; CHECK-NEXT:    call void @init_mem(ptr [[SRC]], i64 128)
+; CHECK-NEXT:    br label %[[VECTOR_PH:.*]]
+; CHECK:       [[VECTOR_PH]]:
+; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
+; CHECK:       [[VECTOR_BODY]]:
+; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY_INTERIM:.*]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr inbounds i8, ptr [[SRC]], i64 [[INDEX]]
+; CHECK-NEXT:    [[WIDE_LOAD:%.*]] = load <4 x i8>, ptr [[TMP0]], align 1
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp eq <4 x i8> [[WIDE_LOAD]], splat (i8 42)
+; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 4
+; CHECK-NEXT:    [[TMP2:%.*]] = freeze <4 x i1> [[TMP1]]
+; CHECK-NEXT:    [[TMP3:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> [[TMP2]])
+; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i64 [[INDEX_NEXT]], 128
+; CHECK-NEXT:    br i1 [[TMP3]], label %[[VECTOR_EARLY_EXIT:.*]], label %[[VECTOR_BODY_INTERIM]]
+; CHECK:       [[VECTOR_BODY_INTERIM]]:
+; CHECK-NEXT:    br i1 [[TMP4]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP19:![0-9]+]]
+; CHECK:       [[MIDDLE_BLOCK]]:
+; CHECK-NEXT:    br label %[[EXIT:.*]]
+; CHECK:       [[VECTOR_EARLY_EXIT]]:
+; CHECK-NEXT:    [[TMP5:%.*]] = call i64 @llvm.experimental.cttz.elts.i64.v4i1(<4 x i1> [[TMP1]], i1 false)
+; CHECK-NEXT:    [[TMP6:%.*]] = add i64 [[INDEX]], [[TMP5]]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RETVAL:%.*]] = phi i64 [ [[TMP6]], %[[VECTOR_EARLY_EXIT]] ], [ 128, %[[MIDDLE_BLOCK]] ]
+; CHECK-NEXT:    ret i64 [[RETVAL]]
+;
+entry:
+  %src = alloca [128 x i8]
+  call void @init_mem(ptr %src, i64 128)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %gep = getelementptr inbounds i8, ptr %src, i64 %iv
+  %l = load i8, ptr %gep, align 1
+  %cmp = icmp eq i8 %l, 42
+  br i1 %cmp, label %exit, label %loop.latch
+
+loop.latch:
+  %iv.next = add i64 %iv, 1
+  %ec = icmp eq i64 %iv.next, 128
+  br i1 %ec, label %exit, label %loop.header
+
+exit:
+  %retval = phi i64 [ %iv, %loop.header ], [ 128, %loop.latch ]
+  ret i64 %retval
+}



More information about the llvm-commits mailing list