[llvm] [VectorCombine] Handle widening/narrowing bitcasts in foldShuffleToIdentity (PR #187870)
via llvm-commits
llvm-commits at lists.llvm.org
Tue May 19 23:50:08 PDT 2026
https://github.com/AbdallahRashed updated https://github.com/llvm/llvm-project/pull/187870
>From 738a742b370d34b1aa53046a07ed58c386293b58 Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Sun, 26 Apr 2026 18:48:21 +0200
Subject: [PATCH 1/2] [VectorCombine] Handle widening/narrowing bitcasts in
foldShuffleToIdentity
Track lane indices through vector bitcasts that change element count
in foldShuffleToIdentity. Widening bitcasts (e.g. <2 x i32> -> <4 x i16>)
compress R consecutive destination lanes into one source lane, while
narrowing bitcasts (e.g. <4 x i16> -> <2 x i32>) expand each destination
lane into R source lanes.
Also fix identity check, splat mask, and destination type construction
to use Item.size() instead of Ty->getNumElements(), since the Item
vector changes size when passing through element-count-changing bitcasts.
When foldShuffleToIdentity only traverses a single element-count-changing
bitcast (NumVisited == 2), it just commutes the bitcast and shuffle
without any real simplification. Skip this case to avoid an infinite
loop with foldBitcastShuffle which does the reverse transform.
Fixes #96884.
---
.../Transforms/Vectorize/VectorCombine.cpp | 138 +++++++++++-
.../Transforms/PhaseOrdering/X86/pr67803.ll | 117 +++++++---
.../AArch64/combine-shuffle-ext.ll | 12 +-
.../AArch64/shuffletoidentity.ll | 71 +++++++
.../VectorCombine/X86/shuffle-inseltpoison.ll | 24 +--
.../Transforms/VectorCombine/X86/shuffle.ll | 24 +--
.../X86/shuffletoidentity-bitcast.ll | 201 ++++++++++++++++++
7 files changed, 509 insertions(+), 78 deletions(-)
create mode 100644 llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 5ba344ea9a808..386617dc11358 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -3527,7 +3527,7 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
return FrontV;
}
if (SplatLeafs.contains(std::make_pair(FrontV, From))) {
- SmallVector<int, 16> Mask(Ty->getNumElements(), FrontLane);
+ SmallVector<int, 16> Mask(Item.size(), FrontLane);
return Builder.CreateShuffleVector(FrontV, Mask);
}
if (ConcatLeafs.contains(std::make_pair(FrontV, From))) {
@@ -3551,6 +3551,53 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
}
auto *I = cast<Instruction>(FrontV);
+
+ // Handle vector bitcasts that change element count. We cannot use
+ // generateInstLaneVectorFromOperand for these because the lane indices
+ // don't map 1:1 through the bitcast.
+ if (auto *BitCast = dyn_cast<BitCastInst>(I)) {
+ auto *BCDstTy = dyn_cast<FixedVectorType>(BitCast->getDestTy());
+ auto *BCSrcTy = dyn_cast<FixedVectorType>(BitCast->getSrcTy());
+ if (BCDstTy && BCSrcTy &&
+ BCDstTy->getNumElements() != BCSrcTy->getNumElements()) {
+ unsigned DstElts = BCDstTy->getNumElements();
+ unsigned SrcElts = BCSrcTy->getNumElements();
+ SmallVector<InstLane> NewItem;
+ if (DstElts > SrcElts) {
+ // Widening: compress operand Item.
+ unsigned R = DstElts / SrcElts;
+ if (Item.size() % R != 0)
+ return nullptr;
+ for (unsigned Idx = 0, E = Item.size(); Idx < E; Idx += R) {
+ auto [V, Lane] = Item[Idx];
+ if (!V)
+ NewItem.push_back({nullptr, PoisonMaskElem});
+ else {
+ NewItem.push_back(lookThroughShuffles(
+ cast<Instruction>(V)->getOperand(0), Lane / R));
+ }
+ }
+ } else {
+ // Narrowing: expand operand Item.
+ unsigned R = SrcElts / DstElts;
+ for (auto [V, Lane] : Item) {
+ if (!V) {
+ for (unsigned J = 0; J < R; ++J)
+ NewItem.push_back({nullptr, PoisonMaskElem});
+ } else {
+ Value *Op = cast<Instruction>(V)->getOperand(0);
+ for (unsigned J = 0; J < R; ++J)
+ NewItem.push_back(lookThroughShuffles(Op, Lane * R + J));
+ }
+ }
+ }
+ Value *Op = generateNewInstTree(NewItem, &BitCast->getOperandUse(0), Ty,
+ IdentityLeafs, SplatLeafs, ConcatLeafs,
+ Builder, TTI);
+ return Builder.CreateBitCast(
+ Op, FixedVectorType::get(BCDstTy->getScalarType(), Item.size()));
+ }
+ }
auto *II = dyn_cast<IntrinsicInst>(I);
unsigned NumOps = I->getNumOperands() - (II ? 1 : 0);
SmallVector<Value *> Ops(NumOps);
@@ -3571,7 +3618,7 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
ValueList.push_back(Lane.first);
Type *DstTy =
- FixedVectorType::get(I->getType()->getScalarType(), Ty->getNumElements());
+ FixedVectorType::get(I->getType()->getScalarType(), Item.size());
if (auto *BI = dyn_cast<BinaryOperator>(I)) {
auto *Value = Builder.CreateBinOp((Instruction::BinaryOps)BI->getOpcode(),
Ops[0], Ops[1]);
@@ -3621,6 +3668,7 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
Worklist.push_back(std::make_pair(Start, &*I.use_begin()));
DenseSet<std::pair<Value *, Use *>> IdentityLeafs, SplatLeafs, ConcatLeafs;
unsigned NumVisited = 0;
+ bool TraversedElCountChangingBitcast = false;
while (!Worklist.empty()) {
if (++NumVisited > MaxInstrsToScan)
@@ -3644,7 +3692,7 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
// Look for an identity value.
if (FrontLane == 0 &&
cast<FixedVectorType>(FrontV->getType())->getNumElements() ==
- Ty->getNumElements() &&
+ Item.size() &&
all_of(drop_begin(enumerate(Item)), [IsEquiv, Item](const auto &E) {
Value *FrontV = Item.front().first;
return !E.value().first || (IsEquiv(E.value().first, FrontV) &&
@@ -3725,14 +3773,76 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
&cast<Instruction>(FrontV)->getOperandUse(0));
continue;
} else if (auto *BitCast = dyn_cast<BitCastInst>(FrontV)) {
- // TODO: Handle vector widening/narrowing bitcasts.
- auto *DstTy = dyn_cast<FixedVectorType>(BitCast->getDestTy());
- auto *SrcTy = dyn_cast<FixedVectorType>(BitCast->getSrcTy());
- if (DstTy && SrcTy &&
- SrcTy->getNumElements() == DstTy->getNumElements()) {
- Worklist.emplace_back(generateInstLaneVectorFromOperand(Item, 0),
- &BitCast->getOperandUse(0));
- continue;
+ auto *BCDstTy = dyn_cast<FixedVectorType>(BitCast->getDestTy());
+ auto *BCSrcTy = dyn_cast<FixedVectorType>(BitCast->getSrcTy());
+ if (BCDstTy && BCSrcTy) {
+ unsigned DstElts = BCDstTy->getNumElements();
+ unsigned SrcElts = BCSrcTy->getNumElements();
+ if (DstElts == SrcElts) {
+ // Same element count - simple pass-through.
+ Worklist.emplace_back(generateInstLaneVectorFromOperand(Item, 0),
+ &BitCast->getOperandUse(0));
+ continue;
+ }
+ if (DstElts > SrcElts && DstElts % SrcElts == 0 &&
+ Item.size() % (DstElts / SrcElts) == 0) {
+ // Widening bitcast (e.g. <2 x i32> -> <4 x i16>). Compress
+ // consecutive groups of R destination lanes into one source
+ // lane.
+ unsigned R = DstElts / SrcElts;
+ SmallVector<InstLane> NItem;
+ bool Valid = true;
+ for (unsigned Idx = 0, E = Item.size(); Idx < E; Idx += R) {
+ auto [V0, L0] = Item[Idx];
+ if (!V0) {
+ if (any_of(ArrayRef(Item).slice(Idx + 1, R - 1),
+ [](InstLane IL) { return IL.first != nullptr; })) {
+ Valid = false;
+ break;
+ }
+ NItem.push_back({nullptr, PoisonMaskElem});
+ continue;
+ }
+ if (L0 % R != 0) {
+ Valid = false;
+ break;
+ }
+ for (unsigned J = 1; J < R; ++J) {
+ auto [VJ, LJ] = Item[Idx + J];
+ if (!VJ || VJ != V0 || LJ != L0 + (int)J) {
+ Valid = false;
+ break;
+ }
+ }
+ if (!Valid)
+ break;
+ NItem.push_back(lookThroughShuffles(
+ cast<Instruction>(V0)->getOperand(0), L0 / R));
+ }
+ if (Valid) {
+ TraversedElCountChangingBitcast = true;
+ Worklist.emplace_back(NItem, &BitCast->getOperandUse(0));
+ continue;
+ }
+ } else if (SrcElts > DstElts && SrcElts % DstElts == 0) {
+ // Narrowing bitcast (e.g. <4 x i16> -> <2 x i32>). Expand
+ // each destination lane into R source lanes.
+ unsigned R = SrcElts / DstElts;
+ SmallVector<InstLane> NItem;
+ for (auto [V, Lane] : Item) {
+ if (!V) {
+ for (unsigned J = 0; J < R; ++J)
+ NItem.push_back({nullptr, PoisonMaskElem});
+ continue;
+ }
+ Value *Op = cast<Instruction>(V)->getOperand(0);
+ for (unsigned J = 0; J < R; ++J)
+ NItem.push_back(lookThroughShuffles(Op, Lane * R + J));
+ }
+ TraversedElCountChangingBitcast = true;
+ Worklist.emplace_back(NItem, &BitCast->getOperandUse(0));
+ continue;
+ }
}
} else if (auto *Sel = dyn_cast<SelectInst>(FrontV)) {
Worklist.emplace_back(generateInstLaneVectorFromOperand(Item, 0),
@@ -3775,6 +3885,12 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
if (NumVisited <= 1)
return false;
+ // If the only non-leaf node traversed was a single bitcast that changes
+ // element count, the fold would just commute the bitcast and shuffle.
+ // foldBitcastShuffle does the reverse transform, causing an infinite loop.
+ if (NumVisited == 2 && TraversedElCountChangingBitcast)
+ return false;
+
LLVM_DEBUG(dbgs() << "Found a superfluous identity shuffle: " << I << "\n");
// If we got this far, we know the shuffles are superfluous and can be
diff --git a/llvm/test/Transforms/PhaseOrdering/X86/pr67803.ll b/llvm/test/Transforms/PhaseOrdering/X86/pr67803.ll
index 7b690341fb805..ddbca9c961b1d 100644
--- a/llvm/test/Transforms/PhaseOrdering/X86/pr67803.ll
+++ b/llvm/test/Transforms/PhaseOrdering/X86/pr67803.ll
@@ -1,35 +1,91 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v2 | FileCheck %s
-; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v3 | FileCheck %s
-; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v4 | FileCheck %s
+; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v2 | FileCheck %s --check-prefixes=CHECK,SSE
+; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v3 | FileCheck %s --check-prefixes=CHECK,AVX,AVX2
+; RUN: opt < %s -O3 -S -mtriple=x86_64-- -mcpu=x86-64-v4 | FileCheck %s --check-prefixes=CHECK,AVX,AVX512
define <4 x i64> @PR67803(<4 x i64> %x, <4 x i64> %y, <4 x i64> %a, <4 x i64> %b) {
-; CHECK-LABEL: @PR67803(
-; CHECK-NEXT: entry:
-; CHECK-NEXT: [[TMP0:%.*]] = bitcast <4 x i64> [[X:%.*]] to <8 x i32>
-; CHECK-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[Y:%.*]] to <8 x i32>
-; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt <8 x i32> [[TMP0]], [[TMP1]]
-; CHECK-NEXT: [[TMP3:%.*]] = bitcast <4 x i64> [[A:%.*]] to <32 x i8>
-; CHECK-NEXT: [[TMP4:%.*]] = shufflevector <32 x i8> [[TMP3]], <32 x i8> poison, <16 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>
-; CHECK-NEXT: [[TMP5:%.*]] = bitcast <4 x i64> [[B:%.*]] to <32 x i8>
-; CHECK-NEXT: [[TMP6:%.*]] = shufflevector <32 x i8> [[TMP5]], <32 x i8> poison, <16 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>
-; CHECK-NEXT: [[TMP7:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
-; CHECK-NEXT: [[TMP8:%.*]] = bitcast <16 x i8> [[TMP4]] to <4 x i32>
-; CHECK-NEXT: [[TMP9:%.*]] = bitcast <16 x i8> [[TMP6]] to <4 x i32>
-; CHECK-NEXT: [[TMP10:%.*]] = select <4 x i1> [[TMP7]], <4 x i32> [[TMP9]], <4 x i32> [[TMP8]]
-; CHECK-NEXT: [[TMP11:%.*]] = bitcast <4 x i32> [[TMP10]] to <16 x i8>
-; CHECK-NEXT: [[TMP12:%.*]] = bitcast <4 x i64> [[A]] to <32 x i8>
-; CHECK-NEXT: [[TMP13:%.*]] = shufflevector <32 x i8> [[TMP12]], <32 x i8> poison, <16 x i32> <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: [[TMP14:%.*]] = bitcast <4 x i64> [[B]] to <32 x i8>
-; CHECK-NEXT: [[TMP15:%.*]] = shufflevector <32 x i8> [[TMP14]], <32 x i8> poison, <16 x i32> <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: [[TMP16:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
-; CHECK-NEXT: [[TMP17:%.*]] = bitcast <16 x i8> [[TMP13]] to <4 x i32>
-; CHECK-NEXT: [[TMP18:%.*]] = bitcast <16 x i8> [[TMP15]] to <4 x i32>
-; CHECK-NEXT: [[TMP19:%.*]] = select <4 x i1> [[TMP16]], <4 x i32> [[TMP18]], <4 x i32> [[TMP17]]
-; CHECK-NEXT: [[TMP20:%.*]] = bitcast <4 x i32> [[TMP19]] to <16 x i8>
-; CHECK-NEXT: [[TMP21:%.*]] = shufflevector <16 x i8> [[TMP11]], <16 x i8> [[TMP20]], <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: [[SHUFFLE_I23:%.*]] = bitcast <32 x i8> [[TMP21]] to <4 x i64>
-; CHECK-NEXT: ret <4 x i64> [[SHUFFLE_I23]]
+; SSE-LABEL: @PR67803(
+; SSE-NEXT: entry:
+; SSE-NEXT: [[TMP0:%.*]] = bitcast <4 x i64> [[X:%.*]] to <8 x i32>
+; SSE-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[Y:%.*]] to <8 x i32>
+; SSE-NEXT: [[TMP2:%.*]] = icmp sgt <8 x i32> [[TMP0]], [[TMP1]]
+; SSE-NEXT: [[CMP_I21:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; SSE-NEXT: [[SEXT_I22:%.*]] = sext <4 x i1> [[CMP_I21]] to <4 x i32>
+; SSE-NEXT: [[CMP_I:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; SSE-NEXT: [[TMP3:%.*]] = shufflevector <4 x i32> [[SEXT_I22]], <4 x i32> poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison>
+; SSE-NEXT: [[TMP4:%.*]] = bitcast <4 x i64> [[A:%.*]] to <32 x i8>
+; SSE-NEXT: [[TMP5:%.*]] = shufflevector <32 x i8> [[TMP4]], <32 x i8> poison, <16 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>
+; SSE-NEXT: [[TMP6:%.*]] = bitcast <4 x i64> [[B:%.*]] to <32 x i8>
+; SSE-NEXT: [[TMP7:%.*]] = shufflevector <32 x i8> [[TMP6]], <32 x i8> poison, <16 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>
+; SSE-NEXT: [[TMP8:%.*]] = bitcast <8 x i32> [[TMP3]] to <32 x i8>
+; SSE-NEXT: [[TMP9:%.*]] = shufflevector <32 x i8> [[TMP8]], <32 x i8> poison, <16 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>
+; SSE-NEXT: [[TMP10:%.*]] = tail call <16 x i8> @llvm.x86.sse41.pblendvb(<16 x i8> [[TMP5]], <16 x i8> [[TMP7]], <16 x i8> [[TMP9]])
+; SSE-NEXT: [[TMP11:%.*]] = bitcast <4 x i64> [[A]] to <32 x i8>
+; SSE-NEXT: [[TMP12:%.*]] = shufflevector <32 x i8> [[TMP11]], <32 x i8> poison, <16 x i32> <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: [[TMP13:%.*]] = bitcast <4 x i64> [[B]] to <32 x i8>
+; SSE-NEXT: [[TMP14:%.*]] = shufflevector <32 x i8> [[TMP13]], <32 x i8> poison, <16 x i32> <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: [[TMP15:%.*]] = bitcast <16 x i8> [[TMP12]] to <4 x i32>
+; SSE-NEXT: [[TMP16:%.*]] = bitcast <16 x i8> [[TMP14]] to <4 x i32>
+; SSE-NEXT: [[TMP17:%.*]] = select <4 x i1> [[CMP_I]], <4 x i32> [[TMP16]], <4 x i32> [[TMP15]]
+; SSE-NEXT: [[TMP18:%.*]] = bitcast <4 x i32> [[TMP17]] to <16 x i8>
+; SSE-NEXT: [[TMP19:%.*]] = shufflevector <16 x i8> [[TMP10]], <16 x i8> [[TMP18]], <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: [[SHUFFLE_I23:%.*]] = bitcast <32 x i8> [[TMP19]] to <4 x i64>
+; SSE-NEXT: ret <4 x i64> [[SHUFFLE_I23]]
+;
+; AVX2-LABEL: @PR67803(
+; AVX2-NEXT: entry:
+; AVX2-NEXT: [[TMP0:%.*]] = bitcast <4 x i64> [[X:%.*]] to <8 x i32>
+; AVX2-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[Y:%.*]] to <8 x i32>
+; AVX2-NEXT: [[TMP2:%.*]] = icmp sgt <8 x i32> [[TMP0]], [[TMP1]]
+; AVX2-NEXT: [[CMP_I21:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; AVX2-NEXT: [[SEXT_I22:%.*]] = sext <4 x i1> [[CMP_I21]] to <4 x i32>
+; AVX2-NEXT: [[CMP_I:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; AVX2-NEXT: [[TMP3:%.*]] = shufflevector <4 x i32> [[SEXT_I22]], <4 x i32> poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 poison, i32 poison, i32 poison, i32 poison>
+; AVX2-NEXT: [[TMP4:%.*]] = bitcast <4 x i64> [[A:%.*]] to <32 x i8>
+; AVX2-NEXT: [[TMP5:%.*]] = shufflevector <32 x i8> [[TMP4]], <32 x i8> poison, <16 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>
+; AVX2-NEXT: [[TMP6:%.*]] = bitcast <4 x i64> [[B:%.*]] to <32 x i8>
+; AVX2-NEXT: [[TMP7:%.*]] = shufflevector <32 x i8> [[TMP6]], <32 x i8> poison, <16 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>
+; AVX2-NEXT: [[TMP8:%.*]] = bitcast <8 x i32> [[TMP3]] to <32 x i8>
+; AVX2-NEXT: [[TMP9:%.*]] = shufflevector <32 x i8> [[TMP8]], <32 x i8> poison, <16 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>
+; AVX2-NEXT: [[TMP10:%.*]] = tail call <16 x i8> @llvm.x86.sse41.pblendvb(<16 x i8> [[TMP5]], <16 x i8> [[TMP7]], <16 x i8> [[TMP9]])
+; AVX2-NEXT: [[TMP11:%.*]] = bitcast <4 x i64> [[A]] to <32 x i8>
+; AVX2-NEXT: [[TMP12:%.*]] = shufflevector <32 x i8> [[TMP11]], <32 x i8> poison, <16 x i32> <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: [[TMP13:%.*]] = bitcast <4 x i64> [[B]] to <32 x i8>
+; AVX2-NEXT: [[TMP14:%.*]] = shufflevector <32 x i8> [[TMP13]], <32 x i8> poison, <16 x i32> <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: [[TMP15:%.*]] = bitcast <16 x i8> [[TMP12]] to <4 x i32>
+; AVX2-NEXT: [[TMP16:%.*]] = bitcast <16 x i8> [[TMP14]] to <4 x i32>
+; AVX2-NEXT: [[TMP17:%.*]] = select <4 x i1> [[CMP_I]], <4 x i32> [[TMP16]], <4 x i32> [[TMP15]]
+; AVX2-NEXT: [[TMP18:%.*]] = bitcast <4 x i32> [[TMP17]] to <16 x i8>
+; AVX2-NEXT: [[TMP19:%.*]] = shufflevector <16 x i8> [[TMP10]], <16 x i8> [[TMP18]], <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: [[SHUFFLE_I23:%.*]] = bitcast <32 x i8> [[TMP19]] to <4 x i64>
+; AVX2-NEXT: ret <4 x i64> [[SHUFFLE_I23]]
+;
+; AVX512-LABEL: @PR67803(
+; AVX512-NEXT: entry:
+; AVX512-NEXT: [[TMP0:%.*]] = bitcast <4 x i64> [[X:%.*]] to <8 x i32>
+; AVX512-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[Y:%.*]] to <8 x i32>
+; AVX512-NEXT: [[TMP2:%.*]] = icmp sgt <8 x i32> [[TMP0]], [[TMP1]]
+; AVX512-NEXT: [[CMP_I:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; AVX512-NEXT: [[TMP3:%.*]] = bitcast <4 x i64> [[A:%.*]] to <32 x i8>
+; AVX512-NEXT: [[TMP4:%.*]] = shufflevector <32 x i8> [[TMP3]], <32 x i8> poison, <16 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>
+; AVX512-NEXT: [[TMP5:%.*]] = bitcast <4 x i64> [[B:%.*]] to <32 x i8>
+; AVX512-NEXT: [[TMP6:%.*]] = shufflevector <32 x i8> [[TMP5]], <32 x i8> poison, <16 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>
+; AVX512-NEXT: [[TMP7:%.*]] = shufflevector <8 x i1> [[TMP2]], <8 x i1> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; AVX512-NEXT: [[TMP8:%.*]] = bitcast <16 x i8> [[TMP4]] to <4 x i32>
+; AVX512-NEXT: [[TMP9:%.*]] = bitcast <16 x i8> [[TMP6]] to <4 x i32>
+; AVX512-NEXT: [[TMP10:%.*]] = select <4 x i1> [[TMP7]], <4 x i32> [[TMP9]], <4 x i32> [[TMP8]]
+; AVX512-NEXT: [[TMP11:%.*]] = bitcast <4 x i32> [[TMP10]] to <16 x i8>
+; AVX512-NEXT: [[TMP12:%.*]] = bitcast <4 x i64> [[A]] to <32 x i8>
+; AVX512-NEXT: [[TMP13:%.*]] = shufflevector <32 x i8> [[TMP12]], <32 x i8> poison, <16 x i32> <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>
+; AVX512-NEXT: [[TMP14:%.*]] = bitcast <4 x i64> [[B]] to <32 x i8>
+; AVX512-NEXT: [[TMP15:%.*]] = shufflevector <32 x i8> [[TMP14]], <32 x i8> poison, <16 x i32> <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>
+; AVX512-NEXT: [[TMP16:%.*]] = bitcast <16 x i8> [[TMP13]] to <4 x i32>
+; AVX512-NEXT: [[TMP17:%.*]] = bitcast <16 x i8> [[TMP15]] to <4 x i32>
+; AVX512-NEXT: [[TMP18:%.*]] = select <4 x i1> [[CMP_I]], <4 x i32> [[TMP17]], <4 x i32> [[TMP16]]
+; AVX512-NEXT: [[TMP19:%.*]] = bitcast <4 x i32> [[TMP18]] to <16 x i8>
+; AVX512-NEXT: [[TMP20:%.*]] = shufflevector <16 x i8> [[TMP11]], <16 x i8> [[TMP19]], <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>
+; AVX512-NEXT: [[SHUFFLE_I23:%.*]] = bitcast <32 x i8> [[TMP20]] to <4 x i64>
+; AVX512-NEXT: ret <4 x i64> [[SHUFFLE_I23]]
;
entry:
%0 = bitcast <4 x i64> %x to <8 x i32>
@@ -77,3 +133,6 @@ entry:
%shuffle.i23 = shufflevector <2 x i64> %18, <2 x i64> %26, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
ret <4 x i64> %shuffle.i23
}
+;; NOTE: These prefixes are unused and the list is autogenerated. Do not add tests below this line:
+; AVX: {{.*}}
+; CHECK: {{.*}}
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/combine-shuffle-ext.ll b/llvm/test/Transforms/VectorCombine/AArch64/combine-shuffle-ext.ll
index 1503a1b51d256..455c17b60a478 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/combine-shuffle-ext.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/combine-shuffle-ext.ll
@@ -399,9 +399,9 @@ define <4 x i32> @mask_extracts_second_vector_sext(ptr %di, <8 x i16> %other) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[DI]], align 4
; CHECK-NEXT: [[VEC_INS:%.*]] = insertelement <2 x i32> <i32 poison, i32 0>, i32 [[L]], i64 0
-; CHECK-NEXT: [[VEC_BC:%.*]] = bitcast <2 x i32> [[VEC_INS]] to <8 x i8>
-; CHECK-NEXT: [[EXT_1:%.*]] = zext <8 x i8> [[VEC_BC]] to <8 x i16>
-; CHECK-NEXT: [[VEC_SHUFFLE:%.*]] = shufflevector <8 x i16> [[EXT_1]], <8 x i16> [[OTHER]], <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; CHECK-NEXT: [[TMP0:%.*]] = shufflevector <2 x i32> [[VEC_INS]], <2 x i32> poison, <1 x i32> <i32 1>
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <1 x i32> [[TMP0]] to <4 x i8>
+; CHECK-NEXT: [[VEC_SHUFFLE:%.*]] = zext <4 x i8> [[TMP1]] to <4 x i16>
; CHECK-NEXT: [[EXT_2:%.*]] = zext nneg <4 x i16> [[VEC_SHUFFLE]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[EXT_2]]
;
@@ -553,9 +553,9 @@ define <4 x i32> @mask_extracts_second_vector(ptr %di, <8 x i16> %other) {
; CHECK-NEXT: [[ENTRY:.*:]]
; CHECK-NEXT: [[L:%.*]] = load i32, ptr [[DI]], align 4
; CHECK-NEXT: [[VEC_INS:%.*]] = insertelement <2 x i32> <i32 poison, i32 0>, i32 [[L]], i64 0
-; CHECK-NEXT: [[VEC_BC:%.*]] = bitcast <2 x i32> [[VEC_INS]] to <8 x i8>
-; CHECK-NEXT: [[EXT_1:%.*]] = sext <8 x i8> [[VEC_BC]] to <8 x i16>
-; CHECK-NEXT: [[VEC_SHUFFLE:%.*]] = shufflevector <8 x i16> [[EXT_1]], <8 x i16> [[OTHER]], <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+; CHECK-NEXT: [[TMP0:%.*]] = shufflevector <2 x i32> [[VEC_INS]], <2 x i32> poison, <1 x i32> <i32 1>
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <1 x i32> [[TMP0]] to <4 x i8>
+; CHECK-NEXT: [[VEC_SHUFFLE:%.*]] = sext <4 x i8> [[TMP1]] to <4 x i16>
; CHECK-NEXT: [[EXT_2:%.*]] = sext <4 x i16> [[VEC_SHUFFLE]] to <4 x i32>
; CHECK-NEXT: ret <4 x i32> [[EXT_2]]
;
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll b/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
index 72b75148f027e..fddc130975ba2 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
@@ -1356,3 +1356,74 @@ define <48 x i8> @concat_splats(<64 x i8> %wide.vec, <64 x i8> %wide.vec117) {
declare <4 x i64> @llvm.fshl.v4i64(<4 x i64>, <4 x i64>, <4 x i64>)
declare void @use(<4 x i8>)
+
+; Narrowing bitcasts (i16->i32) around a sub, then widening bitcasts (i32->i16)
+; back, split into halves and re-joined. Should fold to a single bitcast+sub+bitcast.
+define <8 x i16> @bitcast_narrowwiden_sub(<8 x i16> %a) {
+; CHECK-LABEL: @bitcast_narrowwiden_sub(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[A:%.*]] to <4 x i32>
+; CHECK-NEXT: [[TMP2:%.*]] = sub <4 x i32> zeroinitializer, [[TMP1]]
+; CHECK-NEXT: [[TMP3:%.*]] = bitcast <4 x i32> [[TMP2]] to <8 x i16>
+; CHECK-NEXT: ret <8 x i16> [[TMP3]]
+;
+ %lo = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ %hi = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+ %bc_lo = bitcast <4 x i16> %lo to <2 x i32>
+ %bc_hi = bitcast <4 x i16> %hi to <2 x i32>
+ %neg_lo = sub <2 x i32> zeroinitializer, %bc_lo
+ %neg_hi = sub <2 x i32> zeroinitializer, %bc_hi
+ %bc_lo2 = bitcast <2 x i32> %neg_lo to <4 x i16>
+ %bc_hi2 = bitcast <2 x i32> %neg_hi to <4 x i16>
+ %result = shufflevector <4 x i16> %bc_lo2, <4 x i16> %bc_hi2, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+ ret <8 x i16> %result
+}
+
+; Two-operand add through narrowing+widening bitcasts.
+define <8 x i16> @bitcast_narrowwiden_add(<8 x i16> %a, <8 x i16> %b) {
+; CHECK-LABEL: @bitcast_narrowwiden_add(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[A:%.*]] to <4 x i32>
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <8 x i16> [[B:%.*]] to <4 x i32>
+; CHECK-NEXT: [[TMP3:%.*]] = add <4 x i32> [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[TMP4:%.*]] = bitcast <4 x i32> [[TMP3]] to <8 x i16>
+; CHECK-NEXT: ret <8 x i16> [[TMP4]]
+;
+ %a_lo = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ %a_hi = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+ %b_lo = shufflevector <8 x i16> %b, <8 x i16> poison, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ %b_hi = shufflevector <8 x i16> %b, <8 x i16> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+ %bc_a_lo = bitcast <4 x i16> %a_lo to <2 x i32>
+ %bc_a_hi = bitcast <4 x i16> %a_hi to <2 x i32>
+ %bc_b_lo = bitcast <4 x i16> %b_lo to <2 x i32>
+ %bc_b_hi = bitcast <4 x i16> %b_hi to <2 x i32>
+ %add_lo = add <2 x i32> %bc_a_lo, %bc_b_lo
+ %add_hi = add <2 x i32> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <2 x i32> %add_lo to <4 x i16>
+ %bc_hi = bitcast <2 x i32> %add_hi to <4 x i16>
+ %result = shufflevector <4 x i16> %bc_lo, <4 x i16> %bc_hi, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+ ret <8 x i16> %result
+}
+
+; Ratio of 4: i8 -> i32 narrowing bitcast with add, then widening back.
+define <16 x i8> @bitcast_narrow4_widen4_add(<16 x i8> %a, <16 x i8> %b) {
+; CHECK-LABEL: @bitcast_narrow4_widen4_add(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <16 x i8> [[A:%.*]] to <4 x i32>
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <16 x i8> [[B:%.*]] to <4 x i32>
+; CHECK-NEXT: [[TMP3:%.*]] = add <4 x i32> [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[TMP4:%.*]] = bitcast <4 x i32> [[TMP3]] to <16 x i8>
+; CHECK-NEXT: ret <16 x i8> [[TMP4]]
+;
+ %a_lo = shufflevector <16 x i8> %a, <16 x i8> poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+ %a_hi = shufflevector <16 x i8> %a, <16 x i8> poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+ %b_lo = shufflevector <16 x i8> %b, <16 x i8> poison, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+ %b_hi = shufflevector <16 x i8> %b, <16 x i8> poison, <8 x i32> <i32 8, i32 9, i32 10, i32 11, i32 12, i32 13, i32 14, i32 15>
+ %bc_a_lo = bitcast <8 x i8> %a_lo to <2 x i32>
+ %bc_a_hi = bitcast <8 x i8> %a_hi to <2 x i32>
+ %bc_b_lo = bitcast <8 x i8> %b_lo to <2 x i32>
+ %bc_b_hi = bitcast <8 x i8> %b_hi to <2 x i32>
+ %add_lo = add <2 x i32> %bc_a_lo, %bc_b_lo
+ %add_hi = add <2 x i32> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <2 x i32> %add_lo to <8 x i8>
+ %bc_hi = bitcast <2 x i32> %add_hi to <8 x i8>
+ %result = shufflevector <8 x i8> %bc_lo, <8 x i8> %bc_hi, <16 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>
+ ret <16 x i8> %result
+}
diff --git a/llvm/test/Transforms/VectorCombine/X86/shuffle-inseltpoison.ll b/llvm/test/Transforms/VectorCombine/X86/shuffle-inseltpoison.ll
index 4c70438b2d6e4..6e099fe22b735 100644
--- a/llvm/test/Transforms/VectorCombine/X86/shuffle-inseltpoison.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/shuffle-inseltpoison.ll
@@ -124,21 +124,17 @@ define <16 x i8> @bitcast_shuf_uses(<4 x i32> %v) {
define <2 x i64> @PR35454_1(<2 x i64> %v) {
; SSE-LABEL: @PR35454_1(
; SSE-NEXT: [[BC:%.*]] = bitcast <2 x i64> [[V:%.*]] to <4 x i32>
-; SSE-NEXT: [[PERMIL:%.*]] = shufflevector <4 x i32> [[BC]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[PERMIL]] to <16 x i8>
+; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[BC]] to <16 x i8>
; SSE-NEXT: [[ADD:%.*]] = shl <16 x i8> [[BC1]], splat (i8 1)
; SSE-NEXT: [[BC2:%.*]] = bitcast <16 x i8> [[ADD]] to <4 x i32>
-; SSE-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; SSE-NEXT: ret <2 x i64> [[BC3]]
;
; AVX-LABEL: @PR35454_1(
; AVX-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[V:%.*]] to <16 x i8>
-; AVX-NEXT: [[BC1:%.*]] = shufflevector <16 x i8> [[TMP1]], <16 x i8> poison, <16 x i32> <i32 12, i32 13, i32 14, i32 15, i32 8, i32 9, i32 10, i32 11, i32 4, i32 5, i32 6, i32 7, i32 0, i32 1, i32 2, i32 3>
-; AVX-NEXT: [[ADD:%.*]] = shl <16 x i8> [[BC1]], splat (i8 1)
+; AVX-NEXT: [[ADD:%.*]] = shl <16 x i8> [[TMP1]], splat (i8 1)
; AVX-NEXT: [[BC2:%.*]] = bitcast <16 x i8> [[ADD]] to <4 x i32>
-; AVX-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; AVX-NEXT: ret <2 x i64> [[BC3]]
;
%bc = bitcast <2 x i64> %v to <4 x i32>
@@ -154,21 +150,17 @@ define <2 x i64> @PR35454_1(<2 x i64> %v) {
define <2 x i64> @PR35454_2(<2 x i64> %v) {
; SSE-LABEL: @PR35454_2(
; SSE-NEXT: [[BC:%.*]] = bitcast <2 x i64> [[V:%.*]] to <4 x i32>
-; SSE-NEXT: [[PERMIL:%.*]] = shufflevector <4 x i32> [[BC]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[PERMIL]] to <8 x i16>
+; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[BC]] to <8 x i16>
; SSE-NEXT: [[ADD:%.*]] = shl <8 x i16> [[BC1]], splat (i16 1)
; SSE-NEXT: [[BC2:%.*]] = bitcast <8 x i16> [[ADD]] to <4 x i32>
-; SSE-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; SSE-NEXT: ret <2 x i64> [[BC3]]
;
; AVX-LABEL: @PR35454_2(
; AVX-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[V:%.*]] to <8 x i16>
-; AVX-NEXT: [[BC1:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 6, i32 7, i32 4, i32 5, i32 2, i32 3, i32 0, i32 1>
-; AVX-NEXT: [[ADD:%.*]] = shl <8 x i16> [[BC1]], splat (i16 1)
+; AVX-NEXT: [[ADD:%.*]] = shl <8 x i16> [[TMP1]], splat (i16 1)
; AVX-NEXT: [[BC2:%.*]] = bitcast <8 x i16> [[ADD]] to <4 x i32>
-; AVX-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; AVX-NEXT: ret <2 x i64> [[BC3]]
;
%bc = bitcast <2 x i64> %v to <4 x i32>
diff --git a/llvm/test/Transforms/VectorCombine/X86/shuffle.ll b/llvm/test/Transforms/VectorCombine/X86/shuffle.ll
index 83e088aaa4e27..eccc8cdcc6d91 100644
--- a/llvm/test/Transforms/VectorCombine/X86/shuffle.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/shuffle.ll
@@ -183,22 +183,18 @@ define <2 x i64> @PR35454_1(<2 x i64> %v) {
; SSE-LABEL: define <2 x i64> @PR35454_1(
; SSE-SAME: <2 x i64> [[V:%.*]]) #[[ATTR0]] {
; SSE-NEXT: [[BC:%.*]] = bitcast <2 x i64> [[V]] to <4 x i32>
-; SSE-NEXT: [[PERMIL:%.*]] = shufflevector <4 x i32> [[BC]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[PERMIL]] to <16 x i8>
+; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[BC]] to <16 x i8>
; SSE-NEXT: [[ADD:%.*]] = shl <16 x i8> [[BC1]], splat (i8 1)
; SSE-NEXT: [[BC2:%.*]] = bitcast <16 x i8> [[ADD]] to <4 x i32>
-; SSE-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; SSE-NEXT: ret <2 x i64> [[BC3]]
;
; AVX-LABEL: define <2 x i64> @PR35454_1(
; AVX-SAME: <2 x i64> [[V:%.*]]) #[[ATTR0]] {
; AVX-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[V]] to <16 x i8>
-; AVX-NEXT: [[BC1:%.*]] = shufflevector <16 x i8> [[TMP1]], <16 x i8> poison, <16 x i32> <i32 12, i32 13, i32 14, i32 15, i32 8, i32 9, i32 10, i32 11, i32 4, i32 5, i32 6, i32 7, i32 0, i32 1, i32 2, i32 3>
-; AVX-NEXT: [[ADD:%.*]] = shl <16 x i8> [[BC1]], splat (i8 1)
+; AVX-NEXT: [[ADD:%.*]] = shl <16 x i8> [[TMP1]], splat (i8 1)
; AVX-NEXT: [[BC2:%.*]] = bitcast <16 x i8> [[ADD]] to <4 x i32>
-; AVX-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; AVX-NEXT: ret <2 x i64> [[BC3]]
;
%bc = bitcast <2 x i64> %v to <4 x i32>
@@ -215,22 +211,18 @@ define <2 x i64> @PR35454_2(<2 x i64> %v) {
; SSE-LABEL: define <2 x i64> @PR35454_2(
; SSE-SAME: <2 x i64> [[V:%.*]]) #[[ATTR0]] {
; SSE-NEXT: [[BC:%.*]] = bitcast <2 x i64> [[V]] to <4 x i32>
-; SSE-NEXT: [[PERMIL:%.*]] = shufflevector <4 x i32> [[BC]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[PERMIL]] to <8 x i16>
+; SSE-NEXT: [[BC1:%.*]] = bitcast <4 x i32> [[BC]] to <8 x i16>
; SSE-NEXT: [[ADD:%.*]] = shl <8 x i16> [[BC1]], splat (i16 1)
; SSE-NEXT: [[BC2:%.*]] = bitcast <8 x i16> [[ADD]] to <4 x i32>
-; SSE-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; SSE-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; SSE-NEXT: ret <2 x i64> [[BC3]]
;
; AVX-LABEL: define <2 x i64> @PR35454_2(
; AVX-SAME: <2 x i64> [[V:%.*]]) #[[ATTR0]] {
; AVX-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[V]] to <8 x i16>
-; AVX-NEXT: [[BC1:%.*]] = shufflevector <8 x i16> [[TMP1]], <8 x i16> poison, <8 x i32> <i32 6, i32 7, i32 4, i32 5, i32 2, i32 3, i32 0, i32 1>
-; AVX-NEXT: [[ADD:%.*]] = shl <8 x i16> [[BC1]], splat (i16 1)
+; AVX-NEXT: [[ADD:%.*]] = shl <8 x i16> [[TMP1]], splat (i16 1)
; AVX-NEXT: [[BC2:%.*]] = bitcast <8 x i16> [[ADD]] to <4 x i32>
-; AVX-NEXT: [[PERMIL1:%.*]] = shufflevector <4 x i32> [[BC2]], <4 x i32> poison, <4 x i32> <i32 3, i32 2, i32 1, i32 0>
-; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[PERMIL1]] to <2 x i64>
+; AVX-NEXT: [[BC3:%.*]] = bitcast <4 x i32> [[BC2]] to <2 x i64>
; AVX-NEXT: ret <2 x i64> [[BC3]]
;
%bc = bitcast <2 x i64> %v to <4 x i32>
diff --git a/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll b/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
new file mode 100644
index 0000000000000..c6d49b4734521
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
@@ -0,0 +1,201 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
+; RUN: opt -passes=vector-combine -S %s -mtriple=x86_64-- -mattr=+sse2 | FileCheck %s --check-prefixes=CHECK,SSE
+; RUN: opt -passes=vector-combine -S %s -mtriple=x86_64-- -mattr=+avx2 | FileCheck %s --check-prefixes=CHECK,AVX
+
+; Tests for foldShuffleToIdentity handling of bitcasts that change element count.
+; Motivated by issue #96884: SSE/AVX code uses vXi64 for __m128i/__m256i types,
+; requiring bitcasts to different element-count types for operations.
+
+; Narrowing bitcast: split <4 x i32> through <2 x i64> bitcasts and rejoin.
+define <4 x i32> @bitcast_i32_to_i64_add(<4 x i32> %a, <4 x i32> %b) {
+; CHECK-LABEL: @bitcast_i32_to_i64_add(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <4 x i32> [[A:%.*]] to <2 x i64>
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <4 x i32> [[B:%.*]] to <2 x i64>
+; CHECK-NEXT: [[TMP3:%.*]] = add <2 x i64> [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[TMP4:%.*]] = bitcast <2 x i64> [[TMP3]] to <4 x i32>
+; CHECK-NEXT: ret <4 x i32> [[TMP4]]
+;
+ %a_lo = shufflevector <4 x i32> %a, <4 x i32> poison, <2 x i32> <i32 0, i32 1>
+ %a_hi = shufflevector <4 x i32> %a, <4 x i32> poison, <2 x i32> <i32 2, i32 3>
+ %b_lo = shufflevector <4 x i32> %b, <4 x i32> poison, <2 x i32> <i32 0, i32 1>
+ %b_hi = shufflevector <4 x i32> %b, <4 x i32> poison, <2 x i32> <i32 2, i32 3>
+ %bc_a_lo = bitcast <2 x i32> %a_lo to <1 x i64>
+ %bc_a_hi = bitcast <2 x i32> %a_hi to <1 x i64>
+ %bc_b_lo = bitcast <2 x i32> %b_lo to <1 x i64>
+ %bc_b_hi = bitcast <2 x i32> %b_hi to <1 x i64>
+ %add_lo = add <1 x i64> %bc_a_lo, %bc_b_lo
+ %add_hi = add <1 x i64> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <1 x i64> %add_lo to <2 x i32>
+ %bc_hi = bitcast <1 x i64> %add_hi to <2 x i32>
+ %result = shufflevector <2 x i32> %bc_lo, <2 x i32> %bc_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ ret <4 x i32> %result
+}
+
+; Widening bitcast: split <2 x i64> through <4 x i32> bitcasts and rejoin.
+; This is the typical SSE pattern: __m128i is <2 x i64>, operations on <4 x i32>.
+define <2 x i64> @bitcast_i64_to_i32_xor(<2 x i64> %a, <2 x i64> %b) {
+; CHECK-LABEL: @bitcast_i64_to_i32_xor(
+; CHECK-NEXT: [[B:%.*]] = xor <2 x i64> [[A:%.*]], [[B1:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <2 x i64> [[B]] to <4 x i32>
+; CHECK-NEXT: [[TMP4:%.*]] = bitcast <4 x i32> [[TMP2]] to <2 x i64>
+; CHECK-NEXT: ret <2 x i64> [[TMP4]]
+;
+ %a_lo = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 0>
+ %a_hi = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 1>
+ %b_lo = shufflevector <2 x i64> %b, <2 x i64> poison, <1 x i32> <i32 0>
+ %b_hi = shufflevector <2 x i64> %b, <2 x i64> poison, <1 x i32> <i32 1>
+ %bc_a_lo = bitcast <1 x i64> %a_lo to <2 x i32>
+ %bc_a_hi = bitcast <1 x i64> %a_hi to <2 x i32>
+ %bc_b_lo = bitcast <1 x i64> %b_lo to <2 x i32>
+ %bc_b_hi = bitcast <1 x i64> %b_hi to <2 x i32>
+ %xor_lo = xor <2 x i32> %bc_a_lo, %bc_b_lo
+ %xor_hi = xor <2 x i32> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <2 x i32> %xor_lo to <1 x i64>
+ %bc_hi = bitcast <2 x i32> %xor_hi to <1 x i64>
+ %result = shufflevector <1 x i64> %bc_lo, <1 x i64> %bc_hi, <2 x i32> <i32 0, i32 1>
+ ret <2 x i64> %result
+}
+
+; i64 -> i16: wider ratio (4x), split and rejoin through <8 x i16> operations.
+define <2 x i64> @bitcast_i64_to_i16_shl(<2 x i64> %a) {
+; CHECK-LABEL: @bitcast_i64_to_i16_shl(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[A:%.*]] to <8 x i16>
+; CHECK-NEXT: [[TMP2:%.*]] = shl <8 x i16> [[TMP1]], splat (i16 3)
+; CHECK-NEXT: [[RESULT:%.*]] = bitcast <8 x i16> [[TMP2]] to <2 x i64>
+; CHECK-NEXT: ret <2 x i64> [[RESULT]]
+;
+ %a_lo = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 0>
+ %a_hi = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 1>
+ %bc_lo = bitcast <1 x i64> %a_lo to <4 x i16>
+ %bc_hi = bitcast <1 x i64> %a_hi to <4 x i16>
+ %shl_lo = shl <4 x i16> %bc_lo, splat (i16 3)
+ %shl_hi = shl <4 x i16> %bc_hi, splat (i16 3)
+ %res_lo = bitcast <4 x i16> %shl_lo to <1 x i64>
+ %res_hi = bitcast <4 x i16> %shl_hi to <1 x i64>
+ %result = shufflevector <1 x i64> %res_lo, <1 x i64> %res_hi, <2 x i32> <i32 0, i32 1>
+ ret <2 x i64> %result
+}
+
+; i64 -> i8: ratio of 8, split __m128i into halves, byte-shift, rejoin.
+define <2 x i64> @bitcast_i64_to_i8_add(<2 x i64> %a, <2 x i64> %b) {
+; CHECK-LABEL: @bitcast_i64_to_i8_add(
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <2 x i64> [[A:%.*]] to <16 x i8>
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <2 x i64> [[B:%.*]] to <16 x i8>
+; CHECK-NEXT: [[TMP3:%.*]] = add <16 x i8> [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[RESULT:%.*]] = bitcast <16 x i8> [[TMP3]] to <2 x i64>
+; CHECK-NEXT: ret <2 x i64> [[RESULT]]
+;
+ %a_lo = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 0>
+ %a_hi = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 1>
+ %b_lo = shufflevector <2 x i64> %b, <2 x i64> poison, <1 x i32> <i32 0>
+ %b_hi = shufflevector <2 x i64> %b, <2 x i64> poison, <1 x i32> <i32 1>
+ %bc_a_lo = bitcast <1 x i64> %a_lo to <8 x i8>
+ %bc_a_hi = bitcast <1 x i64> %a_hi to <8 x i8>
+ %bc_b_lo = bitcast <1 x i64> %b_lo to <8 x i8>
+ %bc_b_hi = bitcast <1 x i64> %b_hi to <8 x i8>
+ %add_lo = add <8 x i8> %bc_a_lo, %bc_b_lo
+ %add_hi = add <8 x i8> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <8 x i8> %add_lo to <1 x i64>
+ %bc_hi = bitcast <8 x i8> %add_hi to <1 x i64>
+ %result = shufflevector <1 x i64> %bc_lo, <1 x i64> %bc_hi, <2 x i32> <i32 0, i32 1>
+ ret <2 x i64> %result
+}
+
+; AVX pattern: <4 x i64> (__m256i) split into two <2 x i64> halves,
+; bitcast to <4 x i32>, operate, bitcast back, rejoin.
+define <4 x i64> @avx_bitcast_i64_to_i32_and(<4 x i64> %a, <4 x i64> %b) {
+; CHECK-LABEL: @avx_bitcast_i64_to_i32_and(
+; CHECK-NEXT: [[B:%.*]] = and <4 x i64> [[A:%.*]], [[B1:%.*]]
+; CHECK-NEXT: [[TMP2:%.*]] = bitcast <4 x i64> [[B]] to <8 x i32>
+; CHECK-NEXT: [[TMP4:%.*]] = bitcast <8 x i32> [[TMP2]] to <4 x i64>
+; CHECK-NEXT: ret <4 x i64> [[TMP4]]
+;
+ %a_lo = shufflevector <4 x i64> %a, <4 x i64> poison, <2 x i32> <i32 0, i32 1>
+ %a_hi = shufflevector <4 x i64> %a, <4 x i64> poison, <2 x i32> <i32 2, i32 3>
+ %b_lo = shufflevector <4 x i64> %b, <4 x i64> poison, <2 x i32> <i32 0, i32 1>
+ %b_hi = shufflevector <4 x i64> %b, <4 x i64> poison, <2 x i32> <i32 2, i32 3>
+ %bc_a_lo = bitcast <2 x i64> %a_lo to <4 x i32>
+ %bc_a_hi = bitcast <2 x i64> %a_hi to <4 x i32>
+ %bc_b_lo = bitcast <2 x i64> %b_lo to <4 x i32>
+ %bc_b_hi = bitcast <2 x i64> %b_hi to <4 x i32>
+ %and_lo = and <4 x i32> %bc_a_lo, %bc_b_lo
+ %and_hi = and <4 x i32> %bc_a_hi, %bc_b_hi
+ %bc_lo = bitcast <4 x i32> %and_lo to <2 x i64>
+ %bc_hi = bitcast <4 x i32> %and_hi to <2 x i64>
+ %result = shufflevector <2 x i64> %bc_lo, <2 x i64> %bc_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ ret <4 x i64> %result
+}
+
+; Compare through bitcast: split, bitcast, icmp, select, bitcast back, rejoin.
+define <4 x i64> @avx_bitcast_select(<4 x i64> %a, <4 x i64> %b) {
+; SSE-LABEL: @avx_bitcast_select(
+; SSE-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[A:%.*]] to <8 x i32>
+; SSE-NEXT: [[TMP2:%.*]] = bitcast <4 x i64> [[B:%.*]] to <8 x i32>
+; SSE-NEXT: [[TMP3:%.*]] = icmp sgt <8 x i32> [[TMP1]], [[TMP2]]
+; SSE-NEXT: [[TMP4:%.*]] = select <8 x i1> [[TMP3]], <8 x i32> [[TMP1]], <8 x i32> [[TMP2]]
+; SSE-NEXT: [[RESULT:%.*]] = bitcast <8 x i32> [[TMP4]] to <4 x i64>
+; SSE-NEXT: ret <4 x i64> [[RESULT]]
+;
+; AVX-LABEL: @avx_bitcast_select(
+; AVX-NEXT: [[TMP1:%.*]] = bitcast <4 x i64> [[A:%.*]] to <8 x i32>
+; AVX-NEXT: [[TMP2:%.*]] = bitcast <4 x i64> [[B:%.*]] to <8 x i32>
+; AVX-NEXT: [[TMP3:%.*]] = icmp sgt <8 x i32> [[TMP1]], [[TMP2]]
+; AVX-NEXT: [[TMP4:%.*]] = bitcast <4 x i64> [[A]] to <8 x i32>
+; AVX-NEXT: [[TMP5:%.*]] = bitcast <4 x i64> [[B]] to <8 x i32>
+; AVX-NEXT: [[TMP6:%.*]] = select <8 x i1> [[TMP3]], <8 x i32> [[TMP4]], <8 x i32> [[TMP5]]
+; AVX-NEXT: [[RESULT:%.*]] = bitcast <8 x i32> [[TMP6]] to <4 x i64>
+; AVX-NEXT: ret <4 x i64> [[RESULT]]
+;
+ %a_lo = shufflevector <4 x i64> %a, <4 x i64> poison, <2 x i32> <i32 0, i32 1>
+ %a_hi = shufflevector <4 x i64> %a, <4 x i64> poison, <2 x i32> <i32 2, i32 3>
+ %b_lo = shufflevector <4 x i64> %b, <4 x i64> poison, <2 x i32> <i32 0, i32 1>
+ %b_hi = shufflevector <4 x i64> %b, <4 x i64> poison, <2 x i32> <i32 2, i32 3>
+ %bc_a_lo = bitcast <2 x i64> %a_lo to <4 x i32>
+ %bc_a_hi = bitcast <2 x i64> %a_hi to <4 x i32>
+ %bc_b_lo = bitcast <2 x i64> %b_lo to <4 x i32>
+ %bc_b_hi = bitcast <2 x i64> %b_hi to <4 x i32>
+ %cmp_lo = icmp sgt <4 x i32> %bc_a_lo, %bc_b_lo
+ %cmp_hi = icmp sgt <4 x i32> %bc_a_hi, %bc_b_hi
+ %sel_lo = select <4 x i1> %cmp_lo, <4 x i32> %bc_a_lo, <4 x i32> %bc_b_lo
+ %sel_hi = select <4 x i1> %cmp_hi, <4 x i32> %bc_a_hi, <4 x i32> %bc_b_hi
+ %bc_lo = bitcast <4 x i32> %sel_lo to <2 x i64>
+ %bc_hi = bitcast <4 x i32> %sel_hi to <2 x i64>
+ %result = shufflevector <2 x i64> %bc_lo, <2 x i64> %bc_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+ ret <4 x i64> %result
+}
+
+; Negative test: misaligned lanes in widening bitcast should not fold.
+define <8 x i16> @bitcast_widen_misaligned(<8 x i16> %a) {
+; CHECK-LABEL: @bitcast_widen_misaligned(
+; CHECK-NEXT: [[LO:%.*]] = shufflevector <8 x i16> [[A:%.*]], <8 x i16> poison, <4 x i32> <i32 1, i32 2, i32 3, i32 4>
+; CHECK-NEXT: [[BC_LO:%.*]] = bitcast <4 x i16> [[LO]] to <2 x i32>
+; CHECK-NEXT: [[TMP1:%.*]] = bitcast <8 x i16> [[A]] to <4 x i32>
+; CHECK-NEXT: [[BC_HI:%.*]] = shufflevector <4 x i32> [[TMP1]], <4 x i32> poison, <2 x i32> <i32 2, i32 3>
+; CHECK-NEXT: [[TMP2:%.*]] = shufflevector <2 x i32> [[BC_LO]], <2 x i32> [[BC_HI]], <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+; CHECK-NEXT: [[TMP3:%.*]] = add <4 x i32> [[TMP2]], [[TMP2]]
+; CHECK-NEXT: [[RESULT:%.*]] = bitcast <4 x i32> [[TMP3]] to <8 x i16>
+; CHECK-NEXT: ret <8 x i16> [[RESULT]]
+;
+ %lo = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 1, i32 2, i32 3, i32 4>
+ %hi = shufflevector <8 x i16> %a, <8 x i16> poison, <4 x i32> <i32 4, i32 5, i32 6, i32 7>
+ %bc_lo = bitcast <4 x i16> %lo to <2 x i32>
+ %bc_hi = bitcast <4 x i16> %hi to <2 x i32>
+ %add_lo = add <2 x i32> %bc_lo, %bc_lo
+ %add_hi = add <2 x i32> %bc_hi, %bc_hi
+ %res_lo = bitcast <2 x i32> %add_lo to <4 x i16>
+ %res_hi = bitcast <2 x i32> %add_hi to <4 x i16>
+ %result = shufflevector <4 x i16> %res_lo, <4 x i16> %res_hi, <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+ ret <8 x i16> %result
+}
+
+; Regression test: bitcast with non-divisible shuffle output size must not crash.
+define <3 x i32> @bitcast_widening_non_divisible(<2 x i64> %x) {
+; CHECK-LABEL: @bitcast_widening_non_divisible(
+; CHECK-NEXT: [[BC:%.*]] = bitcast <2 x i64> [[X:%.*]] to <4 x i32>
+; CHECK-NEXT: [[R:%.*]] = shufflevector <4 x i32> [[BC]], <4 x i32> poison, <3 x i32> <i32 0, i32 1, i32 2>
+; CHECK-NEXT: ret <3 x i32> [[R]]
+;
+ %bc = bitcast <2 x i64> %x to <4 x i32>
+ %r = shufflevector <4 x i32> %bc, <4 x i32> poison, <3 x i32> <i32 0, i32 1, i32 2>
+ ret <3 x i32> %r
+}
>From ea702c58be608feeb6c70ed631a75e76968ab4e1 Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Tue, 19 May 2026 23:21:59 +0200
Subject: [PATCH 2/2] Add big-endian test coverage for bitcast handling in
foldShuffleToIdentity
---
.../Transforms/VectorCombine/AArch64/shuffletoidentity.ll | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll b/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
index fddc130975ba2..099fb53d9d93e 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
@@ -1,7 +1,6 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
-; RUN: opt -passes=vector-combine -S %s | FileCheck %s
-
-target triple = "aarch64"
+; RUN: opt -passes=vector-combine -S %s -mtriple=aarch64-unknown-linux-gnu | FileCheck %s
+; RUN: opt -passes=vector-combine -S %s -mtriple=aarch64_be-unknown-linux-gnu | FileCheck %s
define <8 x i8> @trivial(<8 x i8> %a) {
; CHECK-LABEL: @trivial(
More information about the llvm-commits
mailing list