[llvm] [LV] Tests for combined exit conditions (PR #205104)
Graham Hunter via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 30 03:33:52 PDT 2026
https://github.com/huntergr-arm updated https://github.com/llvm/llvm-project/pull/205104
>From b87ed618aaed4707786cee8e55ea368c4309aff0 Mon Sep 17 00:00:00 2001
From: Graham Hunter <graham.hunter at arm.com>
Date: Fri, 5 Jun 2026 10:19:21 +0000
Subject: [PATCH 1/3] [LV] Tests for combined exit conditions
Some initial tests for loops where earlier scalar transforms have
combined a countable exit with an uncountable one.
---
.../VPlan/early_exit_with_stores_vplan.ll | 23 +++
.../early_exit_combined_exits.ll | 176 ++++++++++++++++++
.../early_exit_store_legality.ll | 24 +++
3 files changed, 223 insertions(+)
create mode 100644 llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
diff --git a/llvm/test/Transforms/LoopVectorize/VPlan/early_exit_with_stores_vplan.ll b/llvm/test/Transforms/LoopVectorize/VPlan/early_exit_with_stores_vplan.ll
index b1abf9c0833b4..822bc1d4f3d93 100644
--- a/llvm/test/Transforms/LoopVectorize/VPlan/early_exit_with_stores_vplan.ll
+++ b/llvm/test/Transforms/LoopVectorize/VPlan/early_exit_with_stores_vplan.ll
@@ -268,3 +268,26 @@ for.inc:
exit:
ret i16 %data
}
+
+define void @combined_exit_conditions(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = select i1 %ee.cmp, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll b/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
new file mode 100644
index 0000000000000..43a62e19eb0c3
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
@@ -0,0 +1,176 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --check-globals none --version 6
+; RUN: opt -S < %s -p loop-vectorize -force-vector-width=4 -force-target-supports-masked-memory-ops -enable-early-exit-vectorization-with-side-effects | FileCheck %s
+
+define void @combined_exit_conditions(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[SCALAR_PH:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY1:.*]]
+; CHECK: [[FOR_BODY1]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[SCALAR_PH]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY1]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[EE_CMP]], i1 true, i1 [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY1]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = select i1 %ee.cmp, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_swap_comparison_order(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_swap_comparison_order(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[COUNTED_CMP]], i1 true, i1 [[EE_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = select i1 %counted.cmp, i1 true, i1 %ee.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_decrementing_iv(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_decrementing_iv(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 19, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = sub nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 0
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[EE_CMP]], i1 true, i1 [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 19, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = sub nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 0
+ %combined.cond = select i1 %ee.cmp, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_no_stores(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_no_stores(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[EE_CMP]], i1 true, i1 [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = select i1 %ee.cmp, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/early_exit_store_legality.ll b/llvm/test/Transforms/LoopVectorize/early_exit_store_legality.ll
index 12065c01d47a4..9b2d26ae84102 100644
--- a/llvm/test/Transforms/LoopVectorize/early_exit_store_legality.ll
+++ b/llvm/test/Transforms/LoopVectorize/early_exit_store_legality.ll
@@ -935,6 +935,30 @@ invalid.block:
unreachable
}
+define void @combined_exit_conditions(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: LV: Checking a loop in 'combined_exit_conditions'
+; CHECK: LV: Not vectorizing: Cannot vectorize uncountable loop.
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = select i1 %ee.cmp, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
declare void @init_mem(ptr, i64);
declare i64 @get_an_unknown_offset();
>From d1947f95c52397eb97e4759bf1778020bc40ca1a Mon Sep 17 00:00:00 2001
From: Graham Hunter <graham.hunter at arm.com>
Date: Wed, 24 Jun 2026 11:10:04 +0000
Subject: [PATCH 2/3] Add phase ordering test
---
...ountable-and-uncountable-exits-combined.ll | 75 +++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 llvm/test/Transforms/PhaseOrdering/AArch64/countable-and-uncountable-exits-combined.ll
diff --git a/llvm/test/Transforms/PhaseOrdering/AArch64/countable-and-uncountable-exits-combined.ll b/llvm/test/Transforms/PhaseOrdering/AArch64/countable-and-uncountable-exits-combined.ll
new file mode 100644
index 0000000000000..2b2f0fbc87762
--- /dev/null
+++ b/llvm/test/Transforms/PhaseOrdering/AArch64/countable-and-uncountable-exits-combined.ll
@@ -0,0 +1,75 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes="default<O3>" -S %s | FileCheck %s
+
+target triple = "aarch64"
+
+; Test derived from the following C code. We see the two exit conditions merged
+; into one before reaching LoopVectorize.
+;
+; int c[10000];
+; int src[10000];
+; int dst[10000];
+;
+; void foo() {
+; for (int i=0; i<10000; ++i) {
+; dst[i] = src[i] + 42;
+; if (c[i])
+; break;
+; }
+; }
+
+ at src = global [10000 x i32] zeroinitializer, align 4
+ at dst = global [10000 x i32] zeroinitializer, align 4
+ at c = global [10000 x i32] zeroinitializer, align 4
+
+define void @foo() #0 {
+; CHECK-LABEL: define void @foo(
+; CHECK-SAME: ) local_unnamed_addr #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[INDVARS_IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds nuw [4 x i8], ptr @src, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[SRC_PTR]], 42
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr @dst, i64 [[INDVARS_IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr @c, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_COND:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[EXITCOND_NOT:%.*]] = icmp eq i64 [[INDVARS_IV_NEXT]], 10000
+; CHECK-NEXT: [[OR_COND:%.*]] = select i1 [[EE_COND]], i1 true, i1 [[EXITCOND_NOT]]
+; CHECK-NEXT: br i1 [[OR_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.cond
+
+for.cond:
+ %iv = phi i32 [ 0, %entry ], [ %inc, %for.inc ]
+ %counted.cmp = icmp samesign ult i32 %iv, 10000
+ br i1 %counted.cmp, label %for.body, label %exit
+
+for.body:
+ %idxprom = zext nneg i32 %iv to i64
+ %arrayidx = getelementptr inbounds nuw [4 x i8], ptr @src, i64 %idxprom
+ %src.ptr = load i32, ptr %arrayidx, align 4
+ %add = add nsw i32 %src.ptr, 42
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr @dst, i64 %idxprom
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr @c, i64 %idxprom
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cond = icmp eq i32 %ee.val, 0
+ br i1 %ee.cond, label %for.inc, label %exit
+
+for.inc:
+ %inc = add nuw nsw i32 %iv, 1
+ br label %for.cond
+
+exit:
+ ret void
+}
+
+attributes #0 = { nounwind "target-features"="+sve2" }
>From 9c7e57c83c5f3872749fa9ee8f33771f1fbec14b Mon Sep 17 00:00:00 2001
From: Graham Hunter <graham.hunter at arm.com>
Date: Tue, 30 Jun 2026 10:31:43 +0000
Subject: [PATCH 3/3] Add tests for: * More than 2 conditions * 2 uncounted
conditions * Logical and * Binary or * Binary and
---
.../early_exit_combined_exits.ll | 232 ++++++++++++++++++
1 file changed, 232 insertions(+)
diff --git a/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll b/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
index 43a62e19eb0c3..f8f561e242bdc 100644
--- a/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
+++ b/llvm/test/Transforms/LoopVectorize/early_exit_combined_exits.ll
@@ -174,3 +174,235 @@ for.body:
exit:
ret void
}
+
+define void @combined_exit_three_conditions_two_uncountable(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred.a, ptr align 4 dereferenceable(80) readonly %pred.b) {
+; CHECK-LABEL: define void @combined_exit_three_conditions_two_uncountable(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED_A:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED_B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_A_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED_A]], i64 [[IV]]
+; CHECK-NEXT: [[EE_A_VAL:%.*]] = load i32, ptr [[EE_A_PTR]], align 4
+; CHECK-NEXT: [[EE_A_CMP:%.*]] = icmp ne i32 [[EE_A_VAL]], 0
+; CHECK-NEXT: [[EE_B_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED_B]], i64 [[IV]]
+; CHECK-NEXT: [[EE_B_VAL:%.*]] = load i32, ptr [[EE_B_PTR]], align 4
+; CHECK-NEXT: [[EE_B_CMP:%.*]] = icmp ugt i32 [[EE_B_VAL]], 100
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[EITHER_UNCOUNTED:%.*]] = select i1 [[EE_A_CMP]], i1 true, i1 [[EE_B_CMP]]
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[EITHER_UNCOUNTED]], i1 true, i1 [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.a.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred.a, i64 %iv
+ %ee.a.val = load i32, ptr %ee.a.ptr, align 4
+ %ee.a.cmp = icmp ne i32 %ee.a.val, 0
+ %ee.b.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred.b, i64 %iv
+ %ee.b.val = load i32, ptr %ee.b.ptr, align 4
+ %ee.b.cmp = icmp ugt i32 %ee.b.val, 100
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %either.uncounted = select i1 %ee.a.cmp, i1 true, i1 %ee.b.cmp
+ %combined.cond = select i1 %either.uncounted, i1 true, i1 %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_two_uncountable_only(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred.a, ptr align 4 dereferenceable(80) readonly %pred.b) {
+; CHECK-LABEL: define void @combined_exit_conditions_two_uncountable_only(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED_A:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED_B:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_A_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED_A]], i64 [[IV]]
+; CHECK-NEXT: [[EE_A_VAL:%.*]] = load i32, ptr [[EE_A_PTR]], align 4
+; CHECK-NEXT: [[EE_A_CMP:%.*]] = icmp ne i32 [[EE_A_VAL]], 0
+; CHECK-NEXT: [[EE_B_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED_B]], i64 [[IV]]
+; CHECK-NEXT: [[EE_B_VAL:%.*]] = load i32, ptr [[EE_B_PTR]], align 4
+; CHECK-NEXT: [[EE_B_CMP:%.*]] = icmp ugt i32 [[EE_B_VAL]], 100
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[EITHER_UNCOUNTED:%.*]] = select i1 [[EE_A_CMP]], i1 true, i1 [[EE_B_CMP]]
+; CHECK-NEXT: br i1 [[EITHER_UNCOUNTED]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.a.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred.a, i64 %iv
+ %ee.a.val = load i32, ptr %ee.a.ptr, align 4
+ %ee.a.cmp = icmp ne i32 %ee.a.val, 0
+ %ee.b.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred.b, i64 %iv
+ %ee.b.val = load i32, ptr %ee.b.ptr, align 4
+ %ee.b.cmp = icmp ugt i32 %ee.b.val, 100
+ %iv.next = add nuw nsw i64 %iv, 1
+ %either.uncounted = select i1 %ee.a.cmp, i1 true, i1 %ee.b.cmp
+ br i1 %either.uncounted, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_logical_and(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_logical_and(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp uge i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = select i1 [[EE_CMP]], i1 [[COUNTED_CMP]], i1 false
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp uge i64 %iv.next, 20
+ %combined.cond = select i1 %ee.cmp, i1 %counted.cmp, i1 false
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_binary_or(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_binary_or(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp eq i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = or i1 [[EE_CMP]], [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp eq i64 %iv.next, 20
+ %combined.cond = or i1 %ee.cmp, %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
+
+define void @combined_exit_conditions_binary_and(ptr align 4 dereferenceable(80) readonly %src, ptr align 4 dereferenceable(80) noalias %dst, ptr align 4 dereferenceable(80) readonly %pred) {
+; CHECK-LABEL: define void @combined_exit_conditions_binary_and(
+; CHECK-SAME: ptr readonly align 4 dereferenceable(80) [[SRC:%.*]], ptr noalias align 4 dereferenceable(80) [[DST:%.*]], ptr readonly align 4 dereferenceable(80) [[PRED:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[FOR_BODY:.*]]
+; CHECK: [[FOR_BODY]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[FOR_BODY]] ]
+; CHECK-NEXT: [[SRC_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[SRC]], i64 [[IV]]
+; CHECK-NEXT: [[DATA:%.*]] = load i32, ptr [[SRC_PTR]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[DATA]], 1
+; CHECK-NEXT: [[DST_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[DST]], i64 [[IV]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[DST_PTR]], align 4
+; CHECK-NEXT: [[EE_PTR:%.*]] = getelementptr inbounds nuw [4 x i8], ptr [[PRED]], i64 [[IV]]
+; CHECK-NEXT: [[EE_VAL:%.*]] = load i32, ptr [[EE_PTR]], align 4
+; CHECK-NEXT: [[EE_CMP:%.*]] = icmp ne i32 [[EE_VAL]], 0
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
+; CHECK-NEXT: [[COUNTED_CMP:%.*]] = icmp uge i64 [[IV_NEXT]], 20
+; CHECK-NEXT: [[COMBINED_COND:%.*]] = and i1 [[EE_CMP]], [[COUNTED_CMP]]
+; CHECK-NEXT: br i1 [[COMBINED_COND]], label %[[EXIT:.*]], label %[[FOR_BODY]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %for.body
+
+for.body:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %for.body ]
+ %src.ptr = getelementptr inbounds nuw [4 x i8], ptr %src, i64 %iv
+ %data = load i32, ptr %src.ptr, align 4
+ %add = add nsw i32 %data, 1
+ %dst.ptr = getelementptr inbounds nuw [4 x i8], ptr %dst, i64 %iv
+ store i32 %add, ptr %dst.ptr, align 4
+ %ee.ptr = getelementptr inbounds nuw [4 x i8], ptr %pred, i64 %iv
+ %ee.val = load i32, ptr %ee.ptr, align 4
+ %ee.cmp = icmp ne i32 %ee.val, 0
+ %iv.next = add nuw nsw i64 %iv, 1
+ %counted.cmp = icmp uge i64 %iv.next, 20
+ %combined.cond = and i1 %ee.cmp, %counted.cmp
+ br i1 %combined.cond, label %exit, label %for.body
+
+exit:
+ ret void
+}
More information about the llvm-commits
mailing list