https://github.com/ParkHanbum updated 
https://github.com/llvm/llvm-project/pull/232587

>From 98eca34f902245f58b92dc1e47540601d3143abb Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Fri, 16 Oct 2026 01:35:11 +0900
Subject: [PATCH 1/6] [VectorCombine] Precommit adjacent vector store tests
 (NFC)

Record the current output for adjacent vector stores using runtime inputs.
---
 .../VectorCombine/X86/adjacent-stores.ll      | 419 ++++++++++++++++++
 1 file changed, 419 insertions(+)
 create mode 100644 llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll

diff --git a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll 
b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
new file mode 100644
index 00000000000000..296dbd90d627dc
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
@@ -0,0 +1,419 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py 
UTC_ARGS: --version 6
+; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64    | FileCheck %s --check-prefixes=CHECK,SSE
+; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v2 | FileCheck %s --check-prefixes=CHECK,SSE
+; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v3 | FileCheck %s --check-prefixes=CHECK,AVX2
+; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v4 | FileCheck %s --check-prefixes=CHECK,AVX512
+; REQUIRES: x86-registered-target
+
+target datalayout = "e-m:e-p:64:64-i64:64-n8:16:32:64-S128"
+
+; Legal mask vectors can also form a store narrower than 128 bits.
+define void @width64(ptr %p, <64 x i1> %x) #0 {
+; CHECK-LABEL: define void @width64(
+; CHECK-SAME: ptr [[P:%.*]], <64 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 
9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 
19, i32 20, i32 21, i32 22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 
29, i32 30, i32 31>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 32, i32 33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 
40, i32 41, i32 42, i32 43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 
50, i32 51, i32 52, i32 53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 
60, i32 61, i32 62, i32 63>
+; CHECK-NEXT:    [[P4:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
+; CHECK-NEXT:    store <32 x i1> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <32 x i1> [[HI]], ptr [[P4]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <64 x i1> %x, <64 x i1> poison, <32 x i32> <i32 0, i32 
1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 
12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 19, i32 20, i32 21, i32 
22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 29, i32 30, i32 31>
+  %hi = shufflevector <64 x i1> %x, <64 x i1> poison, <32 x i32> <i32 32, i32 
33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 40, i32 41, i32 42, i32 
43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 50, i32 51, i32 52, i32 
53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 60, i32 61, i32 62, i32 
63>
+  %p4 = getelementptr inbounds i8, ptr %p, i64 4
+  store <32 x i1> %lo, ptr %p, align 4
+  store <32 x i1> %hi, ptr %p4, align 4
+  ret void
+}
+
+; Concatenating the two halves folds to %x, so no shuffle is charged.
+; This also works with noimplicitfloat and drops invariant.group metadata.
+; The preferred width prevents joining the following 128-bit store.
+define void @width128(ptr %p, <4 x float> %x, <4 x float> %y) #3 {
+; CHECK-LABEL: define void @width128(
+; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[X:%.*]], <4 x float> [[Y:%.*]]) 
#[[ATTR1:[0-9]+]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4, 
!invariant.group [[META0:![0-9]+]]
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4, 
!invariant.group [[META0]]
+; CHECK-NEXT:    store <4 x float> [[Y]], ptr [[P16]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p16 = getelementptr inbounds i8, ptr %p, i64 16
+  store <2 x float> %lo, ptr %p, align 4, !invariant.group !1
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %hi, ptr %p8, align 4, !invariant.group !1
+  store <4 x float> %y, ptr %p16, align 4
+  ret void
+}
+
+; A folded concatenation is profitable for code size too.
+; A 256-bit store still requires target support and a sufficient preferred 
width.
+define void @width256(ptr %p, <8 x float> %x) #2 {
+; CHECK-LABEL: define void @width256(
+; CHECK-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
+; CHECK-NEXT:    store <4 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <4 x float> [[HI]], ptr [[P16]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <8 x float> %x, <8 x float> poison, <4 x i32> <i32 0, 
i32 1, i32 2, i32 3>
+  %hi = shufflevector <8 x float> %x, <8 x float> poison, <4 x i32> <i32 4, 
i32 5, i32 6, i32 7>
+  %p16 = getelementptr inbounds i8, ptr %p, i64 16
+  store <4 x float> %lo, ptr %p, align 4
+  store <4 x float> %hi, ptr %p16, align 4
+  ret void
+}
+
+; A 512-bit store requires both target support and a sufficient preferred 
width.
+define void @width512(ptr %p, <16 x float> %x) #4 {
+; CHECK-LABEL: define void @width512(
+; CHECK-SAME: ptr [[P:%.*]], <16 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; CHECK-NEXT:    [[P32:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 32
+; CHECK-NEXT:    store <8 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <8 x float> [[HI]], ptr [[P32]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <16 x float> %x, <16 x float> poison, <8 x i32> <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+  %hi = shufflevector <16 x float> %x, <16 x float> poison, <8 x i32> <i32 8, 
i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+  %p32 = getelementptr inbounds i8, ptr %p, i64 32
+  store <8 x float> %lo, ptr %p, align 4
+  store <8 x float> %hi, ptr %p32, align 4
+  ret void
+}
+
+; Being below the width limit does not make a 192-bit vector type legal.
+define void @illegal_width192(ptr %p, <6 x float> %x) #4 {
+; CHECK-LABEL: define void @illegal_width192(
+; CHECK-SAME: ptr [[P:%.*]], <6 x float> [[X:%.*]]) #[[ATTR3]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <6 x float> [[X]], <6 x float> 
poison, <3 x i32> <i32 0, i32 1, i32 2>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <6 x float> [[X]], <6 x float> 
poison, <3 x i32> <i32 3, i32 4, i32 5>
+; CHECK-NEXT:    [[P12:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+; CHECK-NEXT:    store <3 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <3 x float> [[HI]], ptr [[P12]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <6 x float> %x, <6 x float> poison, <3 x i32> <i32 0, 
i32 1, i32 2>
+  %hi = shufflevector <6 x float> %x, <6 x float> poison, <3 x i32> <i32 3, 
i32 4, i32 5>
+  %p12 = getelementptr inbounds i8, ptr %p, i64 12
+  store <3 x float> %lo, ptr %p, align 4
+  store <3 x float> %hi, ptr %p12, align 4
+  ret void
+}
+
+; SROA leaves independent vector PHIs stored at offsets 0 and 8, followed by
+; scalar stores. Accept the shuffle plus wide store when its cost ties.
+define void @sroa_phi_stores(ptr %p, i1 %cond, <2 x float> %a0, <2 x float> 
%a1, <2 x float> %b0, <2 x float> %b1, float %time, float %weight) #0 {
+; CHECK-LABEL: define void @sroa_phi_stores(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[COND:%.*]], <2 x float> [[A0:%.*]], <2 x 
float> [[A1:%.*]], <2 x float> [[B0:%.*]], <2 x float> [[B1:%.*]], float 
[[TIME:%.*]], float [[WEIGHT:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:  [[ENTRY:.*:]]
+; CHECK-NEXT:    br i1 [[COND]], label %[[LEFT:.*]], label %[[RIGHT:.*]]
+; CHECK:       [[LEFT]]:
+; CHECK-NEXT:    br label %[[MERGE:.*]]
+; CHECK:       [[RIGHT]]:
+; CHECK-NEXT:    br label %[[MERGE]]
+; CHECK:       [[MERGE]]:
+; CHECK-NEXT:    [[LO:%.*]] = phi <2 x float> [ [[A0]], %[[LEFT]] ], [ [[A1]], 
%[[RIGHT]] ]
+; CHECK-NEXT:    [[HI:%.*]] = phi <2 x float> [ [[B0]], %[[LEFT]] ], [ [[B1]], 
%[[RIGHT]] ]
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 8
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 8
+; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
+; CHECK-NEXT:    store float [[TIME]], ptr [[P16]], align 8
+; CHECK-NEXT:    [[P20:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 20
+; CHECK-NEXT:    store float [[WEIGHT]], ptr [[P20]], align 4
+; CHECK-NEXT:    ret void
+;
+entry:
+  br i1 %cond, label %left, label %right
+
+left:
+  br label %merge
+
+right:
+  br label %merge
+
+merge:
+  %lo = phi <2 x float> [ %a0, %left ], [ %a1, %right ]
+  %hi = phi <2 x float> [ %b0, %left ], [ %b1, %right ]
+  store <2 x float> %lo, ptr %p, align 8
+  %p8 = getelementptr inbounds nuw i8, ptr %p, i64 8
+  store <2 x float> %hi, ptr %p8, align 8
+  %p16 = getelementptr inbounds nuw i8, ptr %p, i64 16
+  store float %time, ptr %p16, align 8
+  %p20 = getelementptr inbounds nuw i8, ptr %p, i64 20
+  store float %weight, ptr %p20, align 4
+  ret void
+}
+
+; A code-size cost tie also permits combining the stores.
+define void @cost_tie256(ptr %p, <4 x float> %a, <4 x float> %b) #2 {
+; CHECK-LABEL: define void @cost_tie256(
+; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) 
#[[ATTR2]] {
+; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
+; CHECK-NEXT:    store <4 x float> [[A]], ptr [[P]], align 4
+; CHECK-NEXT:    store <4 x float> [[B]], ptr [[P16]], align 4
+; CHECK-NEXT:    ret void
+;
+  %p16 = getelementptr inbounds i8, ptr %p, i64 16
+  store <4 x float> %a, ptr %p, align 4
+  store <4 x float> %b, ptr %p16, align 4
+  ret void
+}
+
+; A legal wider store must still be rejected when its shuffle raises the cost.
+define void @cost_increase128(ptr %p, <8 x i8> %a, <8 x i8> %b) #0 {
+; CHECK-LABEL: define void @cost_increase128(
+; CHECK-SAME: ptr [[P:%.*]], <8 x i8> [[A:%.*]], <8 x i8> [[B:%.*]]) 
#[[ATTR0]] {
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <8 x i8> [[A]], ptr [[P]], align 1
+; CHECK-NEXT:    store <8 x i8> [[B]], ptr [[P8]], align 1
+; CHECK-NEXT:    ret void
+;
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <8 x i8> %a, ptr %p, align 1
+  store <8 x i8> %b, ptr %p8, align 1
+  ret void
+}
+
+; A load that may alias the first store blocks sinking it.
+define float @stop_at_load(ptr %p, ptr %q, <4 x float> %x) #0 {
+; CHECK-LABEL: define float @stop_at_load(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], <4 x float> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    [[V:%.*]] = load float, ptr [[Q]], align 4
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret float [[V]]
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4
+  %v = load float, ptr %q, align 4
+  store <2 x float> %hi, ptr %p8, align 4
+  ret float %v
+}
+
+; A store that may alias the first store also blocks sinking it.
+define void @stop_at_other_store(ptr %p, ptr %q, <4 x float> %x, float %y) #0 {
+; CHECK-LABEL: define void @stop_at_other_store(
+; CHECK-SAME: ptr [[P:%.*]], ptr [[Q:%.*]], <4 x float> [[X:%.*]], float 
[[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store float [[Y]], ptr [[Q]], align 4
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4
+  store float %y, ptr %q, align 4
+  store <2 x float> %hi, ptr %p8, align 4
+  ret void
+}
+
+; Accesses that cannot alias the first store do not block sinking it.
+define float @skip_noalias_accesses(ptr %p, ptr noalias %q, <4 x float> %x, 
float %y) #0 {
+; CHECK-LABEL: define float @skip_noalias_accesses(
+; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <4 x float> [[X:%.*]], 
float [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    [[V:%.*]] = load float, ptr [[Q]], align 4
+; CHECK-NEXT:    store float [[Y]], ptr [[Q]], align 4
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret float [[V]]
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4
+  %v = load float, ptr %q, align 4
+  store float %y, ptr %q, align 4
+  store <2 x float> %hi, ptr %p8, align 4
+  ret float %v
+}
+
+; Sinking across a noalias store remains profitable when a concatenation
+; shuffle is needed but the total cost ties. This reduces final store pressure.
+define void @cost_tie_across_noalias_store(ptr %p, ptr noalias %q, <2 x float> 
%a, <2 x float> %b, float %y) #0 {
+; CHECK-LABEL: define void @cost_tie_across_noalias_store(
+; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <2 x float> [[A:%.*]], <2 
x float> [[B:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[A]], ptr [[P]], align 8
+; CHECK-NEXT:    store float [[Y]], ptr [[Q]], align 4
+; CHECK-NEXT:    store <2 x float> [[B]], ptr [[P8]], align 8
+; CHECK-NEXT:    ret void
+;
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %a, ptr %p, align 8
+  store float %y, ptr %q, align 4
+  store <2 x float> %b, ptr %p8, align 8
+  ret void
+}
+
+; Reusing the original vector strictly lowers the cost, so a noalias call
+; between the stores does not prevent the fold.
+define void @cheaper_across_call(ptr %p, ptr noalias %q, <4 x float> %x) #0 {
+; CHECK-LABEL: define void @cheaper_across_call(
+; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <4 x float> [[X:%.*]]) 
#[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 8
+; CHECK-NEXT:    call void @touch(ptr [[Q]])
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 8
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 8
+  call void @touch(ptr %q)
+  store <2 x float> %hi, ptr %p8, align 8
+  ret void
+}
+
+; Reject a tie across a call: keeping both independent inputs live across the
+; call would require an additional spill even though TTI reports equal costs.
+define void @cost_tie_across_call(ptr %p, ptr noalias %q, <2 x float> %a, <2 x 
float> %b) #0 {
+; CHECK-LABEL: define void @cost_tie_across_call(
+; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <2 x float> [[A:%.*]], <2 
x float> [[B:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[A]], ptr [[P]], align 8
+; CHECK-NEXT:    call void @touch(ptr [[Q]])
+; CHECK-NEXT:    store <2 x float> [[B]], ptr [[P8]], align 8
+; CHECK-NEXT:    ret void
+;
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %a, ptr %p, align 8
+  call void @touch(ptr %q)
+  store <2 x float> %b, ptr %p8, align 8
+  ret void
+}
+
+declare void @touch(ptr) nounwind willreturn memory(argmem: readwrite)
+
+; Mixed vector/scalar packing is excluded even for pieces of the same vector.
+define void @mixed(ptr %p, <4 x float> %x) #0 {
+; CHECK-LABEL: define void @mixed(
+; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[V2:%.*]] = extractelement <4 x float> [[X]], i64 2
+; CHECK-NEXT:    [[V3:%.*]] = extractelement <4 x float> [[X]], i64 3
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    [[P12:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store float [[V2]], ptr [[P8]], align 4
+; CHECK-NEXT:    store float [[V3]], ptr [[P12]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %v2 = extractelement <4 x float> %x, i64 2
+  %v3 = extractelement <4 x float> %x, i64 3
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  %p12 = getelementptr inbounds i8, ptr %p, i64 12
+  store <2 x float> %lo, ptr %p, align 4
+  store float %v2, ptr %p8, align 4
+  store float %v3, ptr %p12, align 4
+  ret void
+}
+
+; Different vector sizes do not match the equal-vector pair fold.
+define void @unequal_vectors(ptr %p, <8 x float> %x) #1 {
+; CHECK-LABEL: define void @unequal_vectors(
+; CHECK-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR5:[0-9]+]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <6 x i32> <i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <6 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <8 x float> %x, <8 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <8 x float> %x, <8 x float> poison, <6 x i32> <i32 2, 
i32 3, i32 4, i32 5, i32 6, i32 7>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4
+  store <6 x float> %hi, ptr %p8, align 4
+  ret void
+}
+
+; The new wide store must be revisited in the same invocation so the existing
+; insert-element store fold can reduce this update to one scalar store.
+define void @revisit_new_store(ptr %p, float %x) #0 {
+; CHECK-LABEL: define void @revisit_new_store(
+; CHECK-SAME: ptr [[P:%.*]], float [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[OLD:%.*]] = load <4 x float>, ptr [[P]], align 4
+; CHECK-NEXT:    [[UPDATED:%.*]] = insertelement <4 x float> [[OLD]], float 
[[X]], i32 1
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[UPDATED]], <4 x 
float> poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[UPDATED]], <4 x 
float> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret void
+;
+  %old = load <4 x float>, ptr %p, align 4
+  %updated = insertelement <4 x float> %old, float %x, i32 1
+  %lo = shufflevector <4 x float> %updated, <4 x float> poison, <2 x i32> <i32 
0, i32 1>
+  %hi = shufflevector <4 x float> %updated, <4 x float> poison, <2 x i32> <i32 
2, i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4
+  store <2 x float> %hi, ptr %p8, align 4
+  ret void
+}
+
+; Nontemporal metadata must keep the stores separate despite the free fold.
+define void @nontemporal(ptr %p, <4 x float> %x) #0 {
+; CHECK-LABEL: define void @nontemporal(
+; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[X:%.*]]) #[[ATTR0]] {
+; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4, !nontemporal 
[[META1:![0-9]+]]
+; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    ret void
+;
+  %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
+  %hi = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 2, 
i32 3>
+  %p8 = getelementptr inbounds i8, ptr %p, i64 8
+  store <2 x float> %lo, ptr %p, align 4, !nontemporal !0
+  store <2 x float> %hi, ptr %p8, align 4
+  ret void
+}
+
+attributes #0 = { "prefer-vector-width"="128" }
+attributes #1 = { "prefer-vector-width"="256" }
+attributes #2 = { optsize "prefer-vector-width"="256" }
+attributes #3 = { noimplicitfloat "prefer-vector-width"="128" }
+attributes #4 = { "prefer-vector-width"="512" }
+!0 = !{i32 1}
+!1 = !{}
+;.
+; SSE: [[META0]] = !{}
+; SSE: [[META1]] = !{i32 1}
+;.
+; AVX2: [[META0]] = !{}
+; AVX2: [[META1]] = !{i32 1}
+;.
+; AVX512: [[META0]] = !{}
+; AVX512: [[META1]] = !{i32 1}
+;.
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add 
tests below this line:
+; AVX2: {{.*}}
+; AVX512: {{.*}}
+; SSE: {{.*}}

>From f648b6647910913eb042f76fd4c616411c9202d2 Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Thu, 15 Oct 2026 01:55:14 +0900
Subject: [PATCH 2/6] [VectorCombine] Combine adjacent equal vector stores

VectorCombine leaves adjacent equal-type vector stores produced by SROA
separate even when combining them does not increase the cost.

Find candidates through pointer uses and check safe sinking. Combine when
TTI reports no cost increase, excluding shuffles that fold to existing
values, and revisit the result through the worklist.
---
 .../Transforms/Vectorize/VectorCombine.cpp    | 180 ++++++++++++++++++
 .../VectorCombine/X86/adjacent-stores.ll      | 166 +++++++++-------
 2 files changed, 275 insertions(+), 71 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp 
b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index ff507918b782d6..40a34e076268d9 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -129,6 +129,9 @@ class VectorCombine {
   bool foldSelectsFromBitcast(Instruction &I);
   bool foldBinopOfReductions(Instruction &I);
   bool foldInsertElementsToStores(Instruction &I);
+  bool foldAdjacentStores(StoreInst &First);
+  bool concatStores(StoreInst &First, StoreInst &Second,
+                    FixedVectorType *WideTy, bool CrossesCall);
   bool scalarizeLoad(Instruction &I);
   bool scalarizeLoadExtract(LoadInst *LI, VectorType *VecTy, Value *Ptr);
   bool scalarizeLoadBitcast(LoadInst *LI, VectorType *VecTy, Value *Ptr);
@@ -1946,6 +1949,180 @@ static Align computeAlignmentAfterScalarization(Align 
VectorAlignment,
   return commonAlignment(VectorAlignment, DL.getTypeStoreSize(ScalarType));
 }
 
+/// Find stores to Address + ByteOffset through pointer uses.
+/// Basic block, instruction order, and access legality are checked by callers.
+///
+/// \returns Store candidates in Address's address space, in unspecified order.
+///          Empty if none are found, offset addition overflows, or the search
+///          budget is exceeded; partial results are discarded.
+static SmallVector<StoreInst *, 4>
+findStoresAtOffset(Value *Address, int64_t ByteOffset, const DataLayout &DL,
+                   unsigned MaxInstrsToScan) {
+  int64_t AddressOffset, TargetOffset;
+  Value *Base = GetPointerBaseWithConstantOffset(Address, AddressOffset, DL,
+                                                 /*AllowNonInbounds=*/false);
+  if (AddOverflow(AddressOffset, ByteOffset, TargetOffset))
+    return {};
+
+  unsigned StoreAddrSpace = Address->getType()->getPointerAddressSpace();
+  SmallVector<StoreInst *, 4> Stores;
+  SmallVector<Value *, 4> Pointers{Base};
+  SmallPtrSet<Value *, 4> Visited{Base};
+  unsigned NumUses = 0;
+  while (!Pointers.empty()) {
+    Value *Ptr = Pointers.pop_back_val();
+    int64_t Offset;
+    if (GetPointerBaseWithConstantOffset(Ptr, Offset, DL,
+                                         /*AllowNonInbounds=*/false) != Base)
+      continue;
+    for (User *U : Ptr->users()) {
+      if (++NumUses > MaxInstrsToScan)
+        return {};
+      if (U->getType()->isPointerTy() && Visited.insert(U).second)
+        Pointers.push_back(U);
+      auto *Store = dyn_cast<StoreInst>(U);
+      if (Offset == TargetOffset && Store &&
+          Store->getPointerOperand() == Ptr &&
+          Store->getPointerAddressSpace() == StoreAddrSpace)
+        Stores.push_back(Store);
+    }
+  }
+  return Stores;
+}
+
+/// Concatenate two equal vector stores at consecutive addresses without
+/// increasing their cost.
+///
+/// \code
+/// Before (S is the store size of either N-lane vector, in bytes):
+///
+///   V0: [ a0 ... aN-1 ] --> First:  store to %p
+///                         ... intervening instructions ...
+///   V1: [ b0 ... bN-1 ] --> Second: store to %p + S
+///
+/// After (the intervening instructions remain in place):
+///
+///   V0 ----+
+///          +--> shuffle [ V0 lanes | V1 lanes ] --> wide store to %p
+///   V1 ----+                                      (at Second)
+/// \endcode
+///
+/// Steps:
+///   1. Validate First and the scan budget.
+///   2. Check the wide store's target legality.
+///   3. Find and validate the consecutive store candidate.
+///   4. Check the intervening instructions for safe sinking.
+///   5. Cost and emit the replacement, then schedule it for revisiting.
+bool VectorCombine::foldAdjacentStores(StoreInst &First) {
+  // Step 1: Require a supported store, a fixed vector without store padding,
+  // and enough scan budget for the two stores.
+  unsigned MaxInstrsToScan = Opts.vector_combine_max_scan_instrs;
+  auto IsSupportedStore = [](const StoreInst &Store) {
+    return Store.isSimple() && !Store.getMetadata(LLVMContext::MD_nontemporal);
+  };
+  if (MaxInstrsToScan < 2 || !IsSupportedStore(First))
+    return false;
+
+  auto *Ty = dyn_cast<FixedVectorType>(First.getValueOperand()->getType());
+  if (!Ty || !DL->typeSizeEqualsStoreSize(Ty))
+    return false;
+
+  // Step 2: Check the wide type, target width, store chain, and alignment.
+  auto *WideTy = FixedVectorType::getDoubleElementsVectorType(Ty);
+  uint64_t WideStoreSizeInBits = DL->getTypeStoreSizeInBits(WideTy);
+  unsigned StoreAddrSpace = First.getPointerAddressSpace();
+  if (WideStoreSizeInBits > TTI.getLoadStoreVecRegBitWidth(StoreAddrSpace))
+    return false;
+
+  uint64_t WideStoreSizeInBytes = DL->getTypeStoreSize(WideTy);
+  Align Alignment = First.getAlign();
+  if (!TTI.isTypeLegal(WideTy) ||
+      !TTI.isLegalToVectorizeStoreChain(WideStoreSizeInBytes, Alignment,
+                                        StoreAddrSpace) ||
+      (Alignment < DL->getABITypeAlign(WideTy) &&
+       !TTI.allowsMisalignedMemoryAccesses(F.getContext(), WideStoreSizeInBits,
+                                           StoreAddrSpace, Alignment)))
+    return false;
+
+  // Step 3: Choose the earliest later store to the next address in this block,
+  // and require a supported access with the same vector type.
+  StoreInst *Second = nullptr;
+  for (StoreInst *Store :
+       findStoresAtOffset(First.getPointerOperand(),
+                          static_cast<int64_t>(DL->getTypeStoreSize(Ty)), *DL,
+                          MaxInstrsToScan))
+    if (Store->getParent() == First.getParent() && First.comesBefore(Store) &&
+        (!Second || Store->comesBefore(Second)))
+      Second = Store;
+  if (!Second || !IsSupportedStore(*Second) ||
+      Second->getValueOperand()->getType() != Ty)
+    return false;
+
+  // Step 4: Within the scan limit, sinking First must cross no implicit exit
+  // or instruction that may read or write the memory accessed by First.
+  MemoryLocation FirstLoc = MemoryLocation::get(&First);
+  unsigned Scanned = 2;
+  bool CrossesCall = false;
+  for (Instruction *I = First.getNextNode(); I != Second;
+       I = I->getNextNode()) {
+    if (isa<PseudoProbeInst>(I))
+      continue;
+    if (++Scanned > MaxInstrsToScan ||
+        !isGuaranteedToTransferExecutionToSuccessor(I) ||
+        isModOrRefSet(AA.getModRefInfo(I, FirstLoc)))
+      return false;
+    CrossesCall |= isa<CallBase>(I);
+  }
+
+  // Step 5: Cost and emit the replacement at Second, then queue it for
+  // revisiting. Across calls, require a strict improvement to allow for 
spills.
+  return concatStores(First, *Second, WideTy, CrossesCall);
+}
+
+/// Replace equal-type stores to consecutive addresses with a concatenation and
+/// wide store at no extra cost, using First's address/alignment at Second.
+/// The caller must have checked target legality and safe sinking to Second.
+///
+/// \param[in,out] First Lower-address store; erased on success.
+/// \param[in,out] Second Adjacent higher-address store; erased on success.
+/// \param WideTy Same element type as the inputs, with twice as many lanes.
+/// \param CrossesCall Whether sinking First crosses a call; ties are rejected.
+/// \returns True if both stores were replaced; false leaves IR unchanged.
+bool VectorCombine::concatStores(StoreInst &First, StoreInst &Second,
+                                 FixedVectorType *WideTy, bool CrossesCall) {
+  auto *Ty = cast<FixedVectorType>(First.getValueOperand()->getType());
+  unsigned StoreAddrSpace = First.getPointerAddressSpace();
+  Align Alignment = First.getAlign();
+  Value *V0 = First.getValueOperand(), *V1 = Second.getValueOperand();
+  auto Mask = createSequentialMask(0, WideTy->getNumElements(), 0);
+  Value *Packed = simplifyShuffleVectorInst(V0, V1, Mask, WideTy, SQ);
+  InstructionCost OldCost = 0;
+  for (StoreInst *Store : {&First, &Second})
+    OldCost += TTI.getMemoryOpCost(Instruction::Store, Ty, Store->getAlign(),
+                                   StoreAddrSpace, CostKind);
+  InstructionCost NewCost = TTI.getMemoryOpCost(
+      Instruction::Store, WideTy, Alignment, StoreAddrSpace, CostKind);
+  if (!Packed)
+    NewCost += TTI.getShuffleCost(TTI::SK_PermuteTwoSrc, WideTy, Ty, CostKind,
+                                  Mask, 0, nullptr, {V0, V1});
+  if (!OldCost.isValid() || !NewCost.isValid() || NewCost > OldCost ||
+      (CrossesCall && NewCost == OldCost))
+    return false;
+
+  Builder.SetInsertPoint(&Second);
+  if (!Packed)
+    Packed = Builder.CreateShuffleVector(V0, V1, Mask);
+  auto *NewStore =
+      Builder.CreateAlignedStore(Packed, First.getPointerOperand(), Alignment);
+  propagateMetadata(NewStore, {&First, &Second});
+  Worklist.pushValue(Packed);
+  if (NextInst == &Second)
+    NextInst = Second.getNextNode();
+  eraseInstruction(First);
+  eraseInstruction(Second);
+  return true;
+}
+
 /// Fold a vector store fed by a single-use insertelement chain into scalar
 /// stores.
 ///
@@ -7061,6 +7238,9 @@ bool VectorCombine::run() {
     if (TryEarlyFoldsOnly)
       return false;
 
+    if (Opcode == Instruction::Store && foldAdjacentStores(cast<StoreInst>(I)))
+      return true;
+
     if (Opcode == Instruction::Call)
       if (foldBitOrderReverseAndSwap(I))
         return true;
diff --git a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll 
b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
index 296dbd90d627dc..821d5904becb37 100644
--- a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
@@ -9,14 +9,28 @@ target datalayout = "e-m:e-p:64:64-i64:64-n8:16:32:64-S128"
 
 ; Legal mask vectors can also form a store narrower than 128 bits.
 define void @width64(ptr %p, <64 x i1> %x) #0 {
-; CHECK-LABEL: define void @width64(
-; CHECK-SAME: ptr [[P:%.*]], <64 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 
9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 
19, i32 20, i32 21, i32 22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 
29, i32 30, i32 31>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 32, i32 33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 
40, i32 41, i32 42, i32 43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 
50, i32 51, i32 52, i32 53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 
60, i32 61, i32 62, i32 63>
-; CHECK-NEXT:    [[P4:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
-; CHECK-NEXT:    store <32 x i1> [[LO]], ptr [[P]], align 4
-; CHECK-NEXT:    store <32 x i1> [[HI]], ptr [[P4]], align 4
-; CHECK-NEXT:    ret void
+; SSE-LABEL: define void @width64(
+; SSE-SAME: ptr [[P:%.*]], <64 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; SSE-NEXT:    [[LO:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 
9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 
19, i32 20, i32 21, i32 22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 
29, i32 30, i32 31>
+; SSE-NEXT:    [[HI:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 32, i32 33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 
40, i32 41, i32 42, i32 43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 
50, i32 51, i32 52, i32 53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 
60, i32 61, i32 62, i32 63>
+; SSE-NEXT:    [[P4:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
+; SSE-NEXT:    store <32 x i1> [[LO]], ptr [[P]], align 4
+; SSE-NEXT:    store <32 x i1> [[HI]], ptr [[P4]], align 4
+; SSE-NEXT:    ret void
+;
+; AVX2-LABEL: define void @width64(
+; AVX2-SAME: ptr [[P:%.*]], <64 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; AVX2-NEXT:    [[LO:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 
9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 
19, i32 20, i32 21, i32 22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 
29, i32 30, i32 31>
+; AVX2-NEXT:    [[HI:%.*]] = shufflevector <64 x i1> [[X]], <64 x i1> poison, 
<32 x i32> <i32 32, i32 33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 
40, i32 41, i32 42, i32 43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 
50, i32 51, i32 52, i32 53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 
60, i32 61, i32 62, i32 63>
+; AVX2-NEXT:    [[P4:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 4
+; AVX2-NEXT:    store <32 x i1> [[LO]], ptr [[P]], align 4
+; AVX2-NEXT:    store <32 x i1> [[HI]], ptr [[P4]], align 4
+; AVX2-NEXT:    ret void
+;
+; AVX512-LABEL: define void @width64(
+; AVX512-SAME: ptr [[P:%.*]], <64 x i1> [[X:%.*]]) #[[ATTR0:[0-9]+]] {
+; AVX512-NEXT:    store <64 x i1> [[X]], ptr [[P]], align 4
+; AVX512-NEXT:    ret void
 ;
   %lo = shufflevector <64 x i1> %x, <64 x i1> poison, <32 x i32> <i32 0, i32 
1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8, i32 9, i32 10, i32 11, i32 
12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18, i32 19, i32 20, i32 21, i32 
22, i32 23, i32 24, i32 25, i32 26, i32 27, i32 28, i32 29, i32 30, i32 31>
   %hi = shufflevector <64 x i1> %x, <64 x i1> poison, <32 x i32> <i32 32, i32 
33, i32 34, i32 35, i32 36, i32 37, i32 38, i32 39, i32 40, i32 41, i32 42, i32 
43, i32 44, i32 45, i32 46, i32 47, i32 48, i32 49, i32 50, i32 51, i32 52, i32 
53, i32 54, i32 55, i32 56, i32 57, i32 58, i32 59, i32 60, i32 61, i32 62, i32 
63>
@@ -32,12 +46,8 @@ define void @width64(ptr %p, <64 x i1> %x) #0 {
 define void @width128(ptr %p, <4 x float> %x, <4 x float> %y) #3 {
 ; CHECK-LABEL: define void @width128(
 ; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[X:%.*]], <4 x float> [[Y:%.*]]) 
#[[ATTR1:[0-9]+]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
 ; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4, 
!invariant.group [[META0:![0-9]+]]
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4, 
!invariant.group [[META0]]
+; CHECK-NEXT:    store <4 x float> [[X]], ptr [[P]], align 4
 ; CHECK-NEXT:    store <4 x float> [[Y]], ptr [[P16]], align 4
 ; CHECK-NEXT:    ret void
 ;
@@ -54,14 +64,24 @@ define void @width128(ptr %p, <4 x float> %x, <4 x float> 
%y) #3 {
 ; A folded concatenation is profitable for code size too.
 ; A 256-bit store still requires target support and a sufficient preferred 
width.
 define void @width256(ptr %p, <8 x float> %x) #2 {
-; CHECK-LABEL: define void @width256(
-; CHECK-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR2:[0-9]+]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
-; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
-; CHECK-NEXT:    store <4 x float> [[LO]], ptr [[P]], align 4
-; CHECK-NEXT:    store <4 x float> [[HI]], ptr [[P16]], align 4
-; CHECK-NEXT:    ret void
+; SSE-LABEL: define void @width256(
+; SSE-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; SSE-NEXT:    [[LO:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; SSE-NEXT:    [[HI:%.*]] = shufflevector <8 x float> [[X]], <8 x float> 
poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; SSE-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
+; SSE-NEXT:    store <4 x float> [[LO]], ptr [[P]], align 4
+; SSE-NEXT:    store <4 x float> [[HI]], ptr [[P16]], align 4
+; SSE-NEXT:    ret void
+;
+; AVX2-LABEL: define void @width256(
+; AVX2-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; AVX2-NEXT:    store <8 x float> [[X]], ptr [[P]], align 4
+; AVX2-NEXT:    ret void
+;
+; AVX512-LABEL: define void @width256(
+; AVX512-SAME: ptr [[P:%.*]], <8 x float> [[X:%.*]]) #[[ATTR2:[0-9]+]] {
+; AVX512-NEXT:    store <8 x float> [[X]], ptr [[P]], align 4
+; AVX512-NEXT:    ret void
 ;
   %lo = shufflevector <8 x float> %x, <8 x float> poison, <4 x i32> <i32 0, 
i32 1, i32 2, i32 3>
   %hi = shufflevector <8 x float> %x, <8 x float> poison, <4 x i32> <i32 4, 
i32 5, i32 6, i32 7>
@@ -73,14 +93,28 @@ define void @width256(ptr %p, <8 x float> %x) #2 {
 
 ; A 512-bit store requires both target support and a sufficient preferred 
width.
 define void @width512(ptr %p, <16 x float> %x) #4 {
-; CHECK-LABEL: define void @width512(
-; CHECK-SAME: ptr [[P:%.*]], <16 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
-; CHECK-NEXT:    [[P32:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 32
-; CHECK-NEXT:    store <8 x float> [[LO]], ptr [[P]], align 4
-; CHECK-NEXT:    store <8 x float> [[HI]], ptr [[P32]], align 4
-; CHECK-NEXT:    ret void
+; SSE-LABEL: define void @width512(
+; SSE-SAME: ptr [[P:%.*]], <16 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; SSE-NEXT:    [[LO:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; SSE-NEXT:    [[HI:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; SSE-NEXT:    [[P32:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 32
+; SSE-NEXT:    store <8 x float> [[LO]], ptr [[P]], align 4
+; SSE-NEXT:    store <8 x float> [[HI]], ptr [[P32]], align 4
+; SSE-NEXT:    ret void
+;
+; AVX2-LABEL: define void @width512(
+; AVX2-SAME: ptr [[P:%.*]], <16 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; AVX2-NEXT:    [[LO:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; AVX2-NEXT:    [[HI:%.*]] = shufflevector <16 x float> [[X]], <16 x float> 
poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+; AVX2-NEXT:    [[P32:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 32
+; AVX2-NEXT:    store <8 x float> [[LO]], ptr [[P]], align 4
+; AVX2-NEXT:    store <8 x float> [[HI]], ptr [[P32]], align 4
+; AVX2-NEXT:    ret void
+;
+; AVX512-LABEL: define void @width512(
+; AVX512-SAME: ptr [[P:%.*]], <16 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
+; AVX512-NEXT:    store <16 x float> [[X]], ptr [[P]], align 4
+; AVX512-NEXT:    ret void
 ;
   %lo = shufflevector <16 x float> %x, <16 x float> poison, <8 x i32> <i32 0, 
i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
   %hi = shufflevector <16 x float> %x, <16 x float> poison, <8 x i32> <i32 8, 
i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
@@ -93,7 +127,7 @@ define void @width512(ptr %p, <16 x float> %x) #4 {
 ; Being below the width limit does not make a 192-bit vector type legal.
 define void @illegal_width192(ptr %p, <6 x float> %x) #4 {
 ; CHECK-LABEL: define void @illegal_width192(
-; CHECK-SAME: ptr [[P:%.*]], <6 x float> [[X:%.*]]) #[[ATTR3]] {
+; CHECK-SAME: ptr [[P:%.*]], <6 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {
 ; CHECK-NEXT:    [[LO:%.*]] = shufflevector <6 x float> [[X]], <6 x float> 
poison, <3 x i32> <i32 0, i32 1, i32 2>
 ; CHECK-NEXT:    [[HI:%.*]] = shufflevector <6 x float> [[X]], <6 x float> 
poison, <3 x i32> <i32 3, i32 4, i32 5>
 ; CHECK-NEXT:    [[P12:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 12
@@ -113,7 +147,7 @@ define void @illegal_width192(ptr %p, <6 x float> %x) #4 {
 ; scalar stores. Accept the shuffle plus wide store when its cost ties.
 define void @sroa_phi_stores(ptr %p, i1 %cond, <2 x float> %a0, <2 x float> 
%a1, <2 x float> %b0, <2 x float> %b1, float %time, float %weight) #0 {
 ; CHECK-LABEL: define void @sroa_phi_stores(
-; CHECK-SAME: ptr [[P:%.*]], i1 [[COND:%.*]], <2 x float> [[A0:%.*]], <2 x 
float> [[A1:%.*]], <2 x float> [[B0:%.*]], <2 x float> [[B1:%.*]], float 
[[TIME:%.*]], float [[WEIGHT:%.*]]) #[[ATTR0]] {
+; CHECK-SAME: ptr [[P:%.*]], i1 [[COND:%.*]], <2 x float> [[A0:%.*]], <2 x 
float> [[A1:%.*]], <2 x float> [[B0:%.*]], <2 x float> [[B1:%.*]], float 
[[TIME:%.*]], float [[WEIGHT:%.*]]) #[[ATTR0:[0-9]+]] {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    br i1 [[COND]], label %[[LEFT:.*]], label %[[RIGHT:.*]]
 ; CHECK:       [[LEFT]]:
@@ -123,9 +157,8 @@ define void @sroa_phi_stores(ptr %p, i1 %cond, <2 x float> 
%a0, <2 x float> %a1,
 ; CHECK:       [[MERGE]]:
 ; CHECK-NEXT:    [[LO:%.*]] = phi <2 x float> [ [[A0]], %[[LEFT]] ], [ [[A1]], 
%[[RIGHT]] ]
 ; CHECK-NEXT:    [[HI:%.*]] = phi <2 x float> [ [[B0]], %[[LEFT]] ], [ [[B1]], 
%[[RIGHT]] ]
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 8
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 8
+; CHECK-NEXT:    [[TMP0:%.*]] = shufflevector <2 x float> [[LO]], <2 x float> 
[[HI]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    store <4 x float> [[TMP0]], ptr [[P]], align 8
 ; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 16
 ; CHECK-NEXT:    store float [[TIME]], ptr [[P16]], align 8
 ; CHECK-NEXT:    [[P20:%.*]] = getelementptr inbounds nuw i8, ptr [[P]], i64 20
@@ -156,12 +189,24 @@ merge:
 
 ; A code-size cost tie also permits combining the stores.
 define void @cost_tie256(ptr %p, <4 x float> %a, <4 x float> %b) #2 {
-; CHECK-LABEL: define void @cost_tie256(
-; CHECK-SAME: ptr [[P:%.*]], <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) 
#[[ATTR2]] {
-; CHECK-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
-; CHECK-NEXT:    store <4 x float> [[A]], ptr [[P]], align 4
-; CHECK-NEXT:    store <4 x float> [[B]], ptr [[P16]], align 4
-; CHECK-NEXT:    ret void
+; SSE-LABEL: define void @cost_tie256(
+; SSE-SAME: ptr [[P:%.*]], <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) 
#[[ATTR2]] {
+; SSE-NEXT:    [[P16:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 16
+; SSE-NEXT:    store <4 x float> [[A]], ptr [[P]], align 4
+; SSE-NEXT:    store <4 x float> [[B]], ptr [[P16]], align 4
+; SSE-NEXT:    ret void
+;
+; AVX2-LABEL: define void @cost_tie256(
+; AVX2-SAME: ptr [[P:%.*]], <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) 
#[[ATTR2]] {
+; AVX2-NEXT:    [[TMP1:%.*]] = shufflevector <4 x float> [[A]], <4 x float> 
[[B]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; AVX2-NEXT:    store <8 x float> [[TMP1]], ptr [[P]], align 4
+; AVX2-NEXT:    ret void
+;
+; AVX512-LABEL: define void @cost_tie256(
+; AVX512-SAME: ptr [[P:%.*]], <4 x float> [[A:%.*]], <4 x float> [[B:%.*]]) 
#[[ATTR2]] {
+; AVX512-NEXT:    [[TMP1:%.*]] = shufflevector <4 x float> [[A]], <4 x float> 
[[B]], <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+; AVX512-NEXT:    store <8 x float> [[TMP1]], ptr [[P]], align 4
+; AVX512-NEXT:    ret void
 ;
   %p16 = getelementptr inbounds i8, ptr %p, i64 16
   store <4 x float> %a, ptr %p, align 4
@@ -230,13 +275,9 @@ define void @stop_at_other_store(ptr %p, ptr %q, <4 x 
float> %x, float %y) #0 {
 define float @skip_noalias_accesses(ptr %p, ptr noalias %q, <4 x float> %x, 
float %y) #0 {
 ; CHECK-LABEL: define float @skip_noalias_accesses(
 ; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <4 x float> [[X:%.*]], 
float [[Y:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
 ; CHECK-NEXT:    [[V:%.*]] = load float, ptr [[Q]], align 4
 ; CHECK-NEXT:    store float [[Y]], ptr [[Q]], align 4
-; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    store <4 x float> [[X]], ptr [[P]], align 4
 ; CHECK-NEXT:    ret float [[V]]
 ;
   %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
@@ -254,10 +295,9 @@ define float @skip_noalias_accesses(ptr %p, ptr noalias 
%q, <4 x float> %x, floa
 define void @cost_tie_across_noalias_store(ptr %p, ptr noalias %q, <2 x float> 
%a, <2 x float> %b, float %y) #0 {
 ; CHECK-LABEL: define void @cost_tie_across_noalias_store(
 ; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <2 x float> [[A:%.*]], <2 
x float> [[B:%.*]], float [[Y:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[A]], ptr [[P]], align 8
 ; CHECK-NEXT:    store float [[Y]], ptr [[Q]], align 4
-; CHECK-NEXT:    store <2 x float> [[B]], ptr [[P8]], align 8
+; CHECK-NEXT:    [[TMP1:%.*]] = shufflevector <2 x float> [[A]], <2 x float> 
[[B]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT:    store <4 x float> [[TMP1]], ptr [[P]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %p8 = getelementptr inbounds i8, ptr %p, i64 8
@@ -272,12 +312,8 @@ define void @cost_tie_across_noalias_store(ptr %p, ptr 
noalias %q, <2 x float> %
 define void @cheaper_across_call(ptr %p, ptr noalias %q, <4 x float> %x) #0 {
 ; CHECK-LABEL: define void @cheaper_across_call(
 ; CHECK-SAME: ptr [[P:%.*]], ptr noalias [[Q:%.*]], <4 x float> [[X:%.*]]) 
#[[ATTR0]] {
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 8
 ; CHECK-NEXT:    call void @touch(ptr [[Q]])
-; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 8
+; CHECK-NEXT:    store <4 x float> [[X]], ptr [[P]], align 8
 ; CHECK-NEXT:    ret void
 ;
   %lo = shufflevector <4 x float> %x, <4 x float> poison, <2 x i32> <i32 0, 
i32 1>
@@ -358,13 +394,8 @@ define void @unequal_vectors(ptr %p, <8 x float> %x) #1 {
 define void @revisit_new_store(ptr %p, float %x) #0 {
 ; CHECK-LABEL: define void @revisit_new_store(
 ; CHECK-SAME: ptr [[P:%.*]], float [[X:%.*]]) #[[ATTR0]] {
-; CHECK-NEXT:    [[OLD:%.*]] = load <4 x float>, ptr [[P]], align 4
-; CHECK-NEXT:    [[UPDATED:%.*]] = insertelement <4 x float> [[OLD]], float 
[[X]], i32 1
-; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[UPDATED]], <4 x 
float> poison, <2 x i32> <i32 0, i32 1>
-; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[UPDATED]], <4 x 
float> poison, <2 x i32> <i32 2, i32 3>
-; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4
-; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr inbounds <4 x float>, ptr [[P]], 
i64 0, i64 1
+; CHECK-NEXT:    store float [[X]], ptr [[TMP1]], align 4
 ; CHECK-NEXT:    ret void
 ;
   %old = load <4 x float>, ptr %p, align 4
@@ -384,7 +415,7 @@ define void @nontemporal(ptr %p, <4 x float> %x) #0 {
 ; CHECK-NEXT:    [[LO:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 0, i32 1>
 ; CHECK-NEXT:    [[HI:%.*]] = shufflevector <4 x float> [[X]], <4 x float> 
poison, <2 x i32> <i32 2, i32 3>
 ; CHECK-NEXT:    [[P8:%.*]] = getelementptr inbounds i8, ptr [[P]], i64 8
-; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4, !nontemporal 
[[META1:![0-9]+]]
+; CHECK-NEXT:    store <2 x float> [[LO]], ptr [[P]], align 4, !nontemporal 
[[META0:![0-9]+]]
 ; CHECK-NEXT:    store <2 x float> [[HI]], ptr [[P8]], align 4
 ; CHECK-NEXT:    ret void
 ;
@@ -404,16 +435,9 @@ attributes #4 = { "prefer-vector-width"="512" }
 !0 = !{i32 1}
 !1 = !{}
 ;.
-; SSE: [[META0]] = !{}
-; SSE: [[META1]] = !{i32 1}
+; SSE: [[META0]] = !{i32 1}
 ;.
-; AVX2: [[META0]] = !{}
-; AVX2: [[META1]] = !{i32 1}
+; AVX2: [[META0]] = !{i32 1}
 ;.
-; AVX512: [[META0]] = !{}
-; AVX512: [[META1]] = !{i32 1}
+; AVX512: [[META0]] = !{i32 1}
 ;.
-;; NOTE: These prefixes are unused and the list is autogenerated. Do not add 
tests below this line:
-; AVX2: {{.*}}
-; AVX512: {{.*}}
-; SSE: {{.*}}

>From 05f5ab853d0ac7fbc022ff59c8c8ab32877f5649 Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Fri, 16 Oct 2026 17:40:43 +0900
Subject: [PATCH 3/6] [Clang][test] Fix AVX record checks after store combining

---
 clang/test/CodeGen/X86/avx-cxx-record.cpp | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/clang/test/CodeGen/X86/avx-cxx-record.cpp 
b/clang/test/CodeGen/X86/avx-cxx-record.cpp
index e64cef32e78ec7..1fb0caead00c3c 100644
--- a/clang/test/CodeGen/X86/avx-cxx-record.cpp
+++ b/clang/test/CodeGen/X86/avx-cxx-record.cpp
@@ -16,9 +16,8 @@ struct XMM2 : XMM1<0>, XMM1<1> {
 // CHECK: define{{.*}} @_Z3foov({{.*}} [[ARG:%.*]]){{.*}}
 // CLANG-20: define{{.*}} <4 x double> @_Z3foov()
 // CHECK: entry:
+// The field stores can be combined; check the sret destination.
 // CHECK-NEXT: store {{.*}}, ptr [[ARG]]{{.*}}
-// CHECK-NEXT: [[TMP1:%.*]] = getelementptr {{.*}}, ptr [[ARG]]{{.*}}
-// CHECK-NEXT: store {{.*}}, ptr [[TMP1]]{{.*}}
 XMM2 foo() {
   XMM2 result;
   ((XMM1<0>*)&result)->x = UInt64x2{1, 2};

>From b42e3d20af8a671a26bc34b512d2a6dcafed5571 Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Thu, 22 Oct 2026 22:52:03 +0900
Subject: [PATCH 4/6] remove x86-registered-target REQUIRES

---
 llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll 
b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
index 821d5904becb37..054fc6fa120132 100644
--- a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
@@ -3,7 +3,6 @@
 ; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v2 | FileCheck %s --check-prefixes=CHECK,SSE
 ; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v3 | FileCheck %s --check-prefixes=CHECK,AVX2
 ; RUN: opt < %s -passes=vector-combine -verify-each -S -mtriple=x86_64-- 
-mcpu=x86-64-v4 | FileCheck %s --check-prefixes=CHECK,AVX512
-; REQUIRES: x86-registered-target
 
 target datalayout = "e-m:e-p:64:64-i64:64-n8:16:32:64-S128"
 

>From c6e682c18a18ec0cbed94b95b139c6b0bae13c26 Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Thu, 22 Oct 2026 23:01:03 +0900
Subject: [PATCH 5/6] Add TODOs for store reshuffling and masked stores

---
 llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll 
b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
index 054fc6fa120132..6a3e845ae35f46 100644
--- a/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/adjacent-stores.ll
@@ -124,6 +124,10 @@ define void @width512(ptr %p, <16 x float> %x) #4 {
 }
 
 ; Being below the width limit does not make a 192-bit vector type legal.
+; TODO: Reshuffle non-power-of-two stores into power-of-two stores
+;       (e.g. 3+3 floats into 4+2).
+; TODO: Merge into a single masked store when profitable
+;       (e.g. a masked 8-lane store for these 6 floats).
 define void @illegal_width192(ptr %p, <6 x float> %x) #4 {
 ; CHECK-LABEL: define void @illegal_width192(
 ; CHECK-SAME: ptr [[P:%.*]], <6 x float> [[X:%.*]]) #[[ATTR3:[0-9]+]] {

>From 45ced9a37e39ab129c773a087a7a219f482742a1 Mon Sep 17 00:00:00 2001
From: hanbeom <[email protected]>
Date: Thu, 22 Oct 2026 23:56:26 +0900
Subject: [PATCH 6/6] Remove manual worklist insertion now handled by IRBuilder

---
 llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp 
b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 40a34e076268d9..bf1fe060888129 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -2115,7 +2115,6 @@ bool VectorCombine::concatStores(StoreInst &First, 
StoreInst &Second,
   auto *NewStore =
       Builder.CreateAlignedStore(Packed, First.getPointerOperand(), Alignment);
   propagateMetadata(NewStore, {&First, &Second});
-  Worklist.pushValue(Packed);
   if (NextInst == &Second)
     NextInst = Second.getNextNode();
   eraseInstruction(First);

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to