[llvm] [SLP][NFC]Add some more tests with phi external uses, NFC (PR #199919)

Alexey Bataev via llvm-commits llvm-commits at lists.llvm.org
Wed May 27 03:27:44 PDT 2026


https://github.com/alexey-bataev created https://github.com/llvm/llvm-project/pull/199919

None

>From fcd84d854ac52187eb287d8d9a02e522a0e0012a Mon Sep 17 00:00:00 2001
From: Alexey Bataev <a.bataev at outlook.com>
Date: Wed, 27 May 2026 03:27:31 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7
---
 .../AArch64/lcssa-phi-inner-loop-scale.ll     | 238 ++++++++++++++++++
 1 file changed, 238 insertions(+)
 create mode 100644 llvm/test/Transforms/SLPVectorizer/AArch64/lcssa-phi-inner-loop-scale.ll

diff --git a/llvm/test/Transforms/SLPVectorizer/AArch64/lcssa-phi-inner-loop-scale.ll b/llvm/test/Transforms/SLPVectorizer/AArch64/lcssa-phi-inner-loop-scale.ll
new file mode 100644
index 0000000000000..83c4dc891b794
--- /dev/null
+++ b/llvm/test/Transforms/SLPVectorizer/AArch64/lcssa-phi-inner-loop-scale.ll
@@ -0,0 +1,238 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=slp-vectorizer -mtriple=aarch64-none-linux-gnu -mcpu=neoverse-n1 -S %s | FileCheck %s
+;
+declare void @use(i32) memory(none) nounwind willreturn
+
+define void @test_correctness(ptr noalias %p, ptr noalias %q, i32 %n) {
+; CHECK-LABEL: define void @test_correctness(
+; CHECK-SAME: ptr noalias [[P:%.*]], ptr noalias [[Q:%.*]], i32 [[N:%.*]]) #[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
+; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[J_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    br label %[[INNER_HEADER:.*]]
+; CHECK:       [[INNER_HEADER]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[OUTER_HEADER]] ], [ [[I_NEXT:%.*]], %[[INNER_BODY:.*]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = phi <2 x i32> [ <i32 10, i32 20>, %[[OUTER_HEADER]] ], [ [[TMP1:%.*]], %[[INNER_BODY]] ]
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[I]], 100
+; CHECK-NEXT:    br i1 [[COND]], label %[[INNER_BODY]], label %[[OUTER_LATCH]]
+; CHECK:       [[INNER_BODY]]:
+; CHECK-NEXT:    [[TMP1]] = add nsw <2 x i32> [[TMP0]], splat (i32 -1)
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[I64:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I64]]
+; CHECK-NEXT:    store <2 x i32> [[TMP1]], ptr [[GEP0]], align 4
+; CHECK-NEXT:    br label %[[INNER_HEADER]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[TMP2:%.*]] = phi <2 x i32> [ [[TMP0]], %[[INNER_HEADER]] ]
+; CHECK-NEXT:    [[TMP3:%.*]] = extractelement <2 x i32> [[TMP2]], i32 0
+; CHECK-NEXT:    [[TMP4:%.*]] = extractelement <2 x i32> [[TMP2]], i32 1
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[TMP3]], [[TMP4]]
+; CHECK-NEXT:    store i32 [[SUM]], ptr [[Q]], align 4
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], 1
+; CHECK-NEXT:    [[OUTER_COND:%.*]] = icmp slt i32 [[J_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[OUTER_COND]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %j = phi i32 [ 0, %entry ], [ %j.next, %outer.latch ]
+  br label %inner.header
+
+inner.header:
+  ; Constant initial values: tree does NOT reach back into the outer loop.
+  %a = phi i32 [ 10, %outer.header ], [ %a.next, %inner.body ]
+  %b = phi i32 [ 20, %outer.header ], [ %b.next, %inner.body ]
+  %i = phi i32 [ 0, %outer.header ], [ %i.next, %inner.body ]
+  %cond = icmp ult i32 %i, 100
+  br i1 %cond, label %inner.body, label %outer.latch
+
+inner.body:
+  %a.next = add nsw i32 %a, -1
+  %b.next = add nsw i32 %b, -1
+  %i.next = add nuw nsw i32 %i, 1
+  %i64  = zext i32 %i to i64
+  %i64n = zext i32 %i.next to i64
+  %gep0 = getelementptr inbounds i32, ptr %p, i64 %i64
+  %gep1 = getelementptr inbounds i32, ptr %p, i64 %i64n
+  store i32 %a.next, ptr %gep0, align 4
+  store i32 %b.next, ptr %gep1, align 4
+  br label %inner.header
+
+; outer.latch is AFTER inner.body: LCSSA-phi users are the only external
+; users of %a/%b. The fix changes their scale from outer_TC=2 to
+; inner_TC*outer_TC=200, raising the extract cost from 8 to 800.
+outer.latch:
+  %v0.lc = phi i32 [ %a, %inner.header ]
+  %v1.lc = phi i32 [ %b, %inner.header ]
+  %sum = add i32 %v0.lc, %v1.lc
+  store i32 %sum, ptr %q, align 4
+  %j.next = add nuw nsw i32 %j, 1
+  %outer.cond = icmp slt i32 %j.next, %n
+  br i1 %outer.cond, label %outer.header, label %exit
+
+exit:
+  ret void
+}
+
+define void @test_ordering_lcssa_first(ptr noalias %p, ptr noalias %q, i32 %n) {
+; CHECK-LABEL: define void @test_ordering_lcssa_first(
+; CHECK-SAME: ptr noalias [[P:%.*]], ptr noalias [[Q:%.*]], i32 [[N:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
+; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[J_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    br label %[[INNER_HEADER:.*]]
+; CHECK:       [[INNER_HEADER]]:
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[OUTER_HEADER]] ], [ [[I_NEXT:%.*]], %[[INNER_BODY:.*]] ]
+; CHECK-NEXT:    [[TMP0:%.*]] = phi <2 x i32> [ <i32 10, i32 20>, %[[OUTER_HEADER]] ], [ [[TMP3:%.*]], %[[INNER_BODY]] ]
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[I]], 100
+; CHECK-NEXT:    br i1 [[COND]], label %[[INNER_BODY]], label %[[OUTER_LATCH]]
+; CHECK:       [[INNER_BODY]]:
+; CHECK-NEXT:    [[TMP1:%.*]] = extractelement <2 x i32> [[TMP0]], i32 0
+; CHECK-NEXT:    call void @use(i32 [[TMP1]])
+; CHECK-NEXT:    [[TMP2:%.*]] = extractelement <2 x i32> [[TMP0]], i32 1
+; CHECK-NEXT:    call void @use(i32 [[TMP2]])
+; CHECK-NEXT:    [[TMP3]] = add nsw <2 x i32> [[TMP0]], splat (i32 -1)
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[I64:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I64]]
+; CHECK-NEXT:    store <2 x i32> [[TMP3]], ptr [[GEP0]], align 4
+; CHECK-NEXT:    br label %[[INNER_HEADER]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[TMP4:%.*]] = phi <2 x i32> [ [[TMP0]], %[[INNER_HEADER]] ]
+; CHECK-NEXT:    [[TMP5:%.*]] = extractelement <2 x i32> [[TMP4]], i32 0
+; CHECK-NEXT:    [[TMP6:%.*]] = extractelement <2 x i32> [[TMP4]], i32 1
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[TMP5]], [[TMP6]]
+; CHECK-NEXT:    store i32 [[SUM]], ptr [[Q]], align 4
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], 1
+; CHECK-NEXT:    [[OUTER_COND:%.*]] = icmp slt i32 [[J_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[OUTER_COND]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %j = phi i32 [ 0, %entry ], [ %j.next, %outer.latch ]
+  br label %inner.header
+
+inner.header:
+  %a = phi i32 [ 10, %outer.header ], [ %a.next, %inner.body ]
+  %b = phi i32 [ 20, %outer.header ], [ %b.next, %inner.body ]
+  %i = phi i32 [ 0, %outer.header ], [ %i.next, %inner.body ]
+  %cond = icmp ult i32 %i, 100
+  br i1 %cond, label %inner.body, label %outer.latch
+
+inner.body:
+  call void @use(i32 %a)
+  call void @use(i32 %b)
+  %a.next = add nsw i32 %a, -1
+  %b.next = add nsw i32 %b, -1
+  %i.next = add nuw nsw i32 %i, 1
+  %i64  = zext i32 %i to i64
+  %i64n = zext i32 %i.next to i64
+  %gep0 = getelementptr inbounds i32, ptr %p, i64 %i64
+  %gep1 = getelementptr inbounds i32, ptr %p, i64 %i64n
+  store i32 %a.next, ptr %gep0, align 4
+  store i32 %b.next, ptr %gep1, align 4
+  br label %inner.header
+
+outer.latch:
+  %v0.lc = phi i32 [ %a, %inner.header ]
+  %v1.lc = phi i32 [ %b, %inner.header ]
+  %sum = add i32 %v0.lc, %v1.lc
+  store i32 %sum, ptr %q, align 4
+  %j.next = add nuw nsw i32 %j, 1
+  %outer.cond = icmp slt i32 %j.next, %n
+  br i1 %outer.cond, label %outer.header, label %exit
+
+exit:
+  ret void
+}
+
+define void @test_ordering_inloop_first(ptr noalias %p, ptr noalias %q, i32 %n) {
+; CHECK-LABEL: define void @test_ordering_inloop_first(
+; CHECK-SAME: ptr noalias [[P:%.*]], ptr noalias [[Q:%.*]], i32 [[N:%.*]]) #[[ATTR1]] {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
+; CHECK:       [[OUTER_HEADER]]:
+; CHECK-NEXT:    [[J:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[J_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    br label %[[INNER_HEADER:.*]]
+; CHECK:       [[INNER_HEADER]]:
+; CHECK-NEXT:    [[A:%.*]] = phi i32 [ 10, %[[OUTER_HEADER]] ], [ [[A_NEXT:%.*]], %[[INNER_BODY:.*]] ]
+; CHECK-NEXT:    [[B:%.*]] = phi i32 [ 20, %[[OUTER_HEADER]] ], [ [[B_NEXT:%.*]], %[[INNER_BODY]] ]
+; CHECK-NEXT:    [[I:%.*]] = phi i32 [ 0, %[[OUTER_HEADER]] ], [ [[I_NEXT:%.*]], %[[INNER_BODY]] ]
+; CHECK-NEXT:    [[COND:%.*]] = icmp ult i32 [[I]], 100
+; CHECK-NEXT:    br i1 [[COND]], label %[[INNER_BODY]], label %[[OUTER_LATCH]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    [[V0_LC:%.*]] = phi i32 [ [[A]], %[[INNER_HEADER]] ]
+; CHECK-NEXT:    [[V1_LC:%.*]] = phi i32 [ [[B]], %[[INNER_HEADER]] ]
+; CHECK-NEXT:    [[SUM:%.*]] = add i32 [[V0_LC]], [[V1_LC]]
+; CHECK-NEXT:    store i32 [[SUM]], ptr [[Q]], align 4
+; CHECK-NEXT:    [[J_NEXT]] = add nuw nsw i32 [[J]], 1
+; CHECK-NEXT:    [[OUTER_COND:%.*]] = icmp slt i32 [[J_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[OUTER_COND]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[INNER_BODY]]:
+; CHECK-NEXT:    call void @use(i32 [[A]])
+; CHECK-NEXT:    call void @use(i32 [[B]])
+; CHECK-NEXT:    [[A_NEXT]] = add nsw i32 [[A]], -1
+; CHECK-NEXT:    [[B_NEXT]] = add nsw i32 [[B]], -1
+; CHECK-NEXT:    [[I_NEXT]] = add nuw nsw i32 [[I]], 1
+; CHECK-NEXT:    [[I64:%.*]] = zext i32 [[I]] to i64
+; CHECK-NEXT:    [[I64N:%.*]] = zext i32 [[I_NEXT]] to i64
+; CHECK-NEXT:    [[GEP0:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I64]]
+; CHECK-NEXT:    [[GEP1:%.*]] = getelementptr inbounds i32, ptr [[P]], i64 [[I64N]]
+; CHECK-NEXT:    store i32 [[A_NEXT]], ptr [[GEP0]], align 4
+; CHECK-NEXT:    store i32 [[B_NEXT]], ptr [[GEP1]], align 4
+; CHECK-NEXT:    br label %[[INNER_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %outer.header
+
+outer.header:
+  %j = phi i32 [ 0, %entry ], [ %j.next, %outer.latch ]
+  br label %inner.header
+
+inner.header:
+  %a = phi i32 [ 10, %outer.header ], [ %a.next, %inner.body ]
+  %b = phi i32 [ 20, %outer.header ], [ %b.next, %inner.body ]
+  %i = phi i32 [ 0, %outer.header ], [ %i.next, %inner.body ]
+  %cond = icmp ult i32 %i, 100
+  br i1 %cond, label %inner.body, label %outer.latch
+
+; outer.latch placed BEFORE inner.body: its LCSSA phis of %a/%b are
+; registered as forward references when parsed, then inner.body @use
+; calls are prepended on top => in-loop users appear first.
+outer.latch:
+  %v0.lc = phi i32 [ %a, %inner.header ]
+  %v1.lc = phi i32 [ %b, %inner.header ]
+  %sum = add i32 %v0.lc, %v1.lc
+  store i32 %sum, ptr %q, align 4
+  %j.next = add nuw nsw i32 %j, 1
+  %outer.cond = icmp slt i32 %j.next, %n
+  br i1 %outer.cond, label %outer.header, label %exit
+
+inner.body:
+  call void @use(i32 %a)
+  call void @use(i32 %b)
+  %a.next = add nsw i32 %a, -1
+  %b.next = add nsw i32 %b, -1
+  %i.next = add nuw nsw i32 %i, 1
+  %i64  = zext i32 %i to i64
+  %i64n = zext i32 %i.next to i64
+  %gep0 = getelementptr inbounds i32, ptr %p, i64 %i64
+  %gep1 = getelementptr inbounds i32, ptr %p, i64 %i64n
+  store i32 %a.next, ptr %gep0, align 4
+  store i32 %b.next, ptr %gep1, align 4
+  br label %inner.header
+
+exit:
+  ret void
+}



More information about the llvm-commits mailing list