[llvm] [VectorCombine] Handle widening/narrowing bitcasts in foldShuffleToIdentity (PR #187870)

Simon Pilgrim via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 8 23:51:56 PDT 2026


https://github.com/RKSimon updated https://github.com/llvm/llvm-project/pull/187870

>From b9316117447af36c146f63c4b3b70d78cbd8e072 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/5] [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 f9b94c52ce3ad..fbadf004fb044 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -3575,7 +3575,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))) {
@@ -3599,6 +3599,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);
@@ -3619,7 +3666,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]);
@@ -3669,6 +3716,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)
@@ -3692,7 +3740,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) &&
@@ -3773,14 +3821,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),
@@ -3823,6 +3933,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 50b52c2016f41..3a923ec82caf6 100644
--- a/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
+++ b/llvm/test/Transforms/VectorCombine/AArch64/shuffletoidentity.ll
@@ -1352,3 +1352,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 e2756fa231157c7b63028cc2d84925df6fc45dcd 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/5] 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 3a923ec82caf6..ab1cd99174399 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(

>From e7eeff8f3fad7b49844a9a4e254830f275c67d13 Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Thu, 4 Jun 2026 19:32:36 +0200
Subject: [PATCH 3/5] Address review comments and improve test coverage

Use append+continue style in bitcast handling paths for both
generateNewInstTree and foldShuffleToIdentity. Move the Item.size() % R
divisibility check from the outer if-condition into the Valid flag,
since the total size is already known from the bitcast.

Add additional edge case tests for bitcast handling:
- One-way widening/narrowing bitcasts (output type differs from input)
- Poison lanes propagation through bitcasts
- Mismatched operations on halves (negative test)
- Intrinsic (abs) through narrowing bitcast
- Single-element source vector with full-ratio compression
---
 .../Transforms/Vectorize/VectorCombine.cpp    |  34 +++--
 .../X86/shuffletoidentity-bitcast.ll          | 135 ++++++++++++++++++
 2 files changed, 151 insertions(+), 18 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index fbadf004fb044..a4c18bdef74be 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -3618,25 +3618,24 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
           return nullptr;
         for (unsigned Idx = 0, E = Item.size(); Idx < E; Idx += R) {
           auto [V, Lane] = Item[Idx];
-          if (!V)
+          if (!V) {
             NewItem.push_back({nullptr, PoisonMaskElem});
-          else {
-            NewItem.push_back(lookThroughShuffles(
-                cast<Instruction>(V)->getOperand(0), Lane / R));
+            continue;
           }
+          NewItem.push_back(
+              lookThroughShuffles(cast<Operator>(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));
+            NewItem.append(R, {nullptr, PoisonMaskElem});
+            continue;
           }
+          Value *Op = cast<Operator>(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,
@@ -3832,15 +3831,15 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
                                   &BitCast->getOperandUse(0));
             continue;
           }
-          if (DstElts > SrcElts && DstElts % SrcElts == 0 &&
-              Item.size() % (DstElts / SrcElts) == 0) {
+          if (DstElts > SrcElts && 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) {
+            bool Valid = Item.size() % R == 0;
+            for (unsigned Idx = 0, E = Item.size(); Valid && Idx < E;
+                 Idx += R) {
               auto [V0, L0] = Item[Idx];
               if (!V0) {
                 if (any_of(ArrayRef(Item).slice(Idx + 1, R - 1),
@@ -3865,7 +3864,7 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
               if (!Valid)
                 break;
               NItem.push_back(lookThroughShuffles(
-                  cast<Instruction>(V0)->getOperand(0), L0 / R));
+                  cast<Operator>(V0)->getOperand(0), L0 / R));
             }
             if (Valid) {
               TraversedElCountChangingBitcast = true;
@@ -3879,11 +3878,10 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
             SmallVector<InstLane> NItem;
             for (auto [V, Lane] : Item) {
               if (!V) {
-                for (unsigned J = 0; J < R; ++J)
-                  NItem.push_back({nullptr, PoisonMaskElem});
+                NItem.append(R, {nullptr, PoisonMaskElem});
                 continue;
               }
-              Value *Op = cast<Instruction>(V)->getOperand(0);
+              Value *Op = cast<Operator>(V)->getOperand(0);
               for (unsigned J = 0; J < R; ++J)
                 NItem.push_back(lookThroughShuffles(Op, Lane * R + J));
             }
diff --git a/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll b/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
index c6d49b4734521..ba145ecf02cb1 100644
--- a/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
+++ b/llvm/test/Transforms/VectorCombine/X86/shuffletoidentity-bitcast.ll
@@ -199,3 +199,138 @@ define <3 x i32> @bitcast_widening_non_divisible(<2 x i64> %x) {
   %r = shufflevector <4 x i32> %bc, <4 x i32> poison, <3 x i32> <i32 0, i32 1, i32 2>
   ret <3 x i32> %r
 }
+
+; One-way widening bitcast: operate in wider type, don't bitcast back.
+; Output type differs from input type.
+define <4 x i32> @bitcast_oneway_widen_or(<2 x i64> %a, <2 x i64> %b) {
+; CHECK-LABEL: @bitcast_oneway_widen_or(
+; CHECK-NEXT:    [[TMP1:%.*]] = or <2 x i64> [[A:%.*]], [[B:%.*]]
+; CHECK-NEXT:    [[RESULT:%.*]] = bitcast <2 x i64> [[TMP1]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[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 <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>
+  %or_lo = or <2 x i32> %bc_a_lo, %bc_b_lo
+  %or_hi = or <2 x i32> %bc_a_hi, %bc_b_hi
+  %result = shufflevector <2 x i32> %or_lo, <2 x i32> %or_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %result
+}
+
+; One-way narrowing bitcast: operate in narrower type, don't bitcast back.
+define <2 x i64> @bitcast_oneway_narrow_sub(<4 x i32> %a, <4 x i32> %b) {
+; CHECK-LABEL: @bitcast_oneway_narrow_sub(
+; CHECK-NEXT:    [[RESULT:%.*]] = bitcast <4 x i32> [[TMP1:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast <4 x i32> [[B:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[RESULT1:%.*]] = sub <2 x i64> [[RESULT]], [[TMP2]]
+; CHECK-NEXT:    ret <2 x i64> [[RESULT1]]
+;
+  %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>
+  %sub_lo = sub <1 x i64> %bc_a_lo, %bc_b_lo
+  %sub_hi = sub <1 x i64> %bc_a_hi, %bc_b_hi
+  %result = shufflevector <1 x i64> %sub_lo, <1 x i64> %sub_hi, <2 x i32> <i32 0, i32 1>
+  ret <2 x i64> %result
+}
+
+; Poison lanes in shuffle through bitcast: ensure poison is preserved.
+define <4 x i32> @bitcast_poison_lanes(<4 x i32> %a) {
+; CHECK-LABEL: @bitcast_poison_lanes(
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <4 x i32> [[A:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[BC_LO:%.*]] = shufflevector <2 x i64> [[TMP1]], <2 x i64> poison, <1 x i32> zeroinitializer
+; CHECK-NEXT:    [[ADD_LO:%.*]] = add <1 x i64> [[BC_LO]], [[BC_LO]]
+; CHECK-NEXT:    [[RES_LO:%.*]] = bitcast <1 x i64> [[ADD_LO]] to <2 x i32>
+; CHECK-NEXT:    [[RESULT:%.*]] = shufflevector <2 x i32> [[RES_LO]], <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+; CHECK-NEXT:    ret <4 x i32> [[RESULT]]
+;
+  %a_lo = shufflevector <4 x i32> %a, <4 x i32> poison, <2 x i32> <i32 0, i32 1>
+  %bc_lo = bitcast <2 x i32> %a_lo to <1 x i64>
+  %add_lo = add <1 x i64> %bc_lo, %bc_lo
+  %res_lo = bitcast <1 x i64> %add_lo to <2 x i32>
+  %result = shufflevector <2 x i32> %res_lo, <2 x i32> poison, <4 x i32> <i32 0, i32 1, i32 poison, i32 poison>
+  ret <4 x i32> %result
+}
+
+; Negative test: different operations on halves should not fold.
+define <4 x i32> @bitcast_different_ops_neg(<4 x i32> %a, <4 x i32> %b) {
+; CHECK-LABEL: @bitcast_different_ops_neg(
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <4 x i32> [[A:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[BC_A_LO:%.*]] = shufflevector <2 x i64> [[TMP1]], <2 x i64> poison, <1 x i32> zeroinitializer
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast <4 x i32> [[A]] to <2 x i64>
+; CHECK-NEXT:    [[BC_A_HI:%.*]] = shufflevector <2 x i64> [[TMP2]], <2 x i64> poison, <1 x i32> <i32 1>
+; CHECK-NEXT:    [[TMP3:%.*]] = bitcast <4 x i32> [[B:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[BC_B_LO:%.*]] = shufflevector <2 x i64> [[TMP3]], <2 x i64> poison, <1 x i32> zeroinitializer
+; CHECK-NEXT:    [[TMP4:%.*]] = bitcast <4 x i32> [[B]] to <2 x i64>
+; CHECK-NEXT:    [[BC_B_HI:%.*]] = shufflevector <2 x i64> [[TMP4]], <2 x i64> poison, <1 x i32> <i32 1>
+; CHECK-NEXT:    [[ADD_LO:%.*]] = add <1 x i64> [[BC_A_LO]], [[BC_B_LO]]
+; CHECK-NEXT:    [[XOR_HI:%.*]] = xor <1 x i64> [[BC_A_HI]], [[BC_B_HI]]
+; CHECK-NEXT:    [[TMP5:%.*]] = shufflevector <1 x i64> [[ADD_LO]], <1 x i64> [[XOR_HI]], <2 x i32> <i32 0, i32 1>
+; CHECK-NEXT:    [[RESULT:%.*]] = bitcast <2 x i64> [[TMP5]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[RESULT]]
+;
+  %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
+  %xor_hi = xor <1 x i64> %bc_a_hi, %bc_b_hi
+  %res_lo = bitcast <1 x i64> %add_lo to <2 x i32>
+  %res_hi = bitcast <1 x i64> %xor_hi to <2 x i32>
+  %result = shufflevector <2 x i32> %res_lo, <2 x i32> %res_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %result
+}
+
+; Bitcast with intrinsic (abs) through narrowing bitcast.
+define <4 x i32> @bitcast_narrow_abs(<4 x i32> %a) {
+; CHECK-LABEL: @bitcast_narrow_abs(
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <4 x i32> [[A:%.*]] to <2 x i64>
+; CHECK-NEXT:    [[TMP2:%.*]] = call <2 x i64> @llvm.abs.v2i64(<2 x i64> [[TMP1]], i1 false)
+; CHECK-NEXT:    [[TMP3:%.*]] = bitcast <2 x i64> [[TMP2]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[TMP3]]
+;
+  %lo = shufflevector <4 x i32> %a, <4 x i32> poison, <2 x i32> <i32 0, i32 1>
+  %hi = shufflevector <4 x i32> %a, <4 x i32> poison, <2 x i32> <i32 2, i32 3>
+  %bc_lo = bitcast <2 x i32> %lo to <1 x i64>
+  %bc_hi = bitcast <2 x i32> %hi to <1 x i64>
+  %abs_lo = call <1 x i64> @llvm.abs.v1i64(<1 x i64> %bc_lo, i1 false)
+  %abs_hi = call <1 x i64> @llvm.abs.v1i64(<1 x i64> %bc_hi, i1 false)
+  %res_lo = bitcast <1 x i64> %abs_lo to <2 x i32>
+  %res_hi = bitcast <1 x i64> %abs_hi to <2 x i32>
+  %result = shufflevector <2 x i32> %res_lo, <2 x i32> %res_hi, <4 x i32> <i32 0, i32 1, i32 2, i32 3>
+  ret <4 x i32> %result
+}
+
+; Single-element source vector: full ratio compression (R equals total elements).
+define <2 x i64> @bitcast_single_src_elt(<2 x i64> %a) {
+; CHECK-LABEL: @bitcast_single_src_elt(
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast <2 x i64> [[A:%.*]] to <8 x i16>
+; CHECK-NEXT:    [[TMP2:%.*]] = add <8 x i16> [[TMP1]], splat (i16 1)
+; CHECK-NEXT:    [[RESULT:%.*]] = bitcast <8 x i16> [[TMP2]] to <2 x i64>
+; CHECK-NEXT:    ret <2 x i64> [[RESULT]]
+;
+  %a0 = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 0>
+  %a1 = shufflevector <2 x i64> %a, <2 x i64> poison, <1 x i32> <i32 1>
+  %bc0 = bitcast <1 x i64> %a0 to <4 x i16>
+  %bc1 = bitcast <1 x i64> %a1 to <4 x i16>
+  %add0 = add <4 x i16> %bc0, splat (i16 1)
+  %add1 = add <4 x i16> %bc1, splat (i16 1)
+  %res0 = bitcast <4 x i16> %add0 to <1 x i64>
+  %res1 = bitcast <4 x i16> %add1 to <1 x i64>
+  %result = shufflevector <1 x i64> %res0, <1 x i64> %res1, <2 x i32> <i32 0, i32 1>
+  ret <2 x i64> %result
+}

>From 64f726f86c10a1834903aec3af6e8b53704f49ca Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Thu, 4 Jun 2026 19:55:49 +0200
Subject: [PATCH 4/5] Use ElementCount for element count comparisons in bitcast
 handling

Use ElementCount instead of unsigned for comparing source and destination
element counts. This is more idiomatic for LLVM vector type APIs.
---
 llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index a4c18bdef74be..ea9ddad29ea1f 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -3607,7 +3607,7 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
     auto *BCDstTy = dyn_cast<FixedVectorType>(BitCast->getDestTy());
     auto *BCSrcTy = dyn_cast<FixedVectorType>(BitCast->getSrcTy());
     if (BCDstTy && BCSrcTy &&
-        BCDstTy->getNumElements() != BCSrcTy->getNumElements()) {
+        BCDstTy->getElementCount() != BCSrcTy->getElementCount()) {
       unsigned DstElts = BCDstTy->getNumElements();
       unsigned SrcElts = BCSrcTy->getNumElements();
       SmallVector<InstLane> NewItem;
@@ -3823,14 +3823,16 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
         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) {
+          ElementCount DstEC = BCDstTy->getElementCount();
+          ElementCount SrcEC = BCSrcTy->getElementCount();
+          if (DstEC == SrcEC) {
             // Same element count - simple pass-through.
             Worklist.emplace_back(generateInstLaneVectorFromOperand(Item, 0),
                                   &BitCast->getOperandUse(0));
             continue;
           }
+          unsigned DstElts = DstEC.getFixedValue();
+          unsigned SrcElts = SrcEC.getFixedValue();
           if (DstElts > SrcElts && DstElts % SrcElts == 0) {
             // Widening bitcast (e.g. <2 x i32> -> <4 x i16>). Compress
             // consecutive groups of R destination lanes into one source

>From 3bbd8019dbcd8c550c00740c16e1134f0d0fce1e Mon Sep 17 00:00:00 2001
From: AbdallahRashed <abdallah.mrashed at gmail.com>
Date: Thu, 9 Jul 2026 00:13:50 +0200
Subject: [PATCH 5/5] ty is not used

---
 llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index ea9ddad29ea1f..747007685dd7a 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -3564,7 +3564,7 @@ static bool isFreeConcat(ArrayRef<InstLane> Item, TTI::TargetCostKind CostKind,
 }
 
 static Value *
-generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
+generateNewInstTree(ArrayRef<InstLane> Item, Use *From,
                     const DenseSet<std::pair<Value *, Use *>> &IdentityLeafs,
                     const DenseSet<std::pair<Value *, Use *>> &SplatLeafs,
                     const DenseSet<std::pair<Value *, Use *>> &ConcatLeafs,
@@ -3638,7 +3638,7 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
             NewItem.push_back(lookThroughShuffles(Op, Lane * R + J));
         }
       }
-      Value *Op = generateNewInstTree(NewItem, &BitCast->getOperandUse(0), Ty,
+      Value *Op = generateNewInstTree(NewItem, &BitCast->getOperandUse(0),
                                       IdentityLeafs, SplatLeafs, ConcatLeafs,
                                       Builder, TTI);
       return Builder.CreateBitCast(
@@ -3655,7 +3655,7 @@ generateNewInstTree(ArrayRef<InstLane> Item, Use *From, FixedVectorType *Ty,
       continue;
     }
     Ops[Idx] = generateNewInstTree(generateInstLaneVectorFromOperand(Item, Idx),
-                                   &I->getOperandUse(Idx), Ty, IdentityLeafs,
+                                   &I->getOperandUse(Idx), IdentityLeafs,
                                    SplatLeafs, ConcatLeafs, Builder, TTI);
   }
 
@@ -3944,7 +3944,7 @@ bool VectorCombine::foldShuffleToIdentity(Instruction &I) {
   // If we got this far, we know the shuffles are superfluous and can be
   // removed. Scan through again and generate the new tree of instructions.
   Builder.SetInsertPoint(&I);
-  Value *V = generateNewInstTree(Start, &*I.use_begin(), Ty, IdentityLeafs,
+  Value *V = generateNewInstTree(Start, &*I.use_begin(), IdentityLeafs,
                                  SplatLeafs, ConcatLeafs, Builder, &TTI);
   replaceValue(I, *V);
   return true;



More information about the llvm-commits mailing list