[llvm] [ConstantFold] Support byte values in `bitcast` constant folding (PR #188030)

Pedro Lobo via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 18 05:12:56 PDT 2026


https://github.com/pedroclobo updated https://github.com/llvm/llvm-project/pull/188030

>From 68b39737a14b150267628aeb6920cdbb26cbe0e5 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 21 Mar 2026 12:18:10 +0000
Subject: [PATCH 01/11] Pre-commit tests

---
 .../InstSimplify/bitcast-vector-fold.ll       | 72 +++++++++++++++++++
 1 file changed, 72 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index d2656e291547c..65bfb61930344 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -292,3 +292,75 @@ define <2 x i64> @bitcast_constexpr_4f32_2i64_1111() {
   %res = bitcast <4 x float> splat (float 1.0) to <2 x i64>
   ret <2 x i64> %res
 }
+
+define <2 x b64> @bitcast_constexpr_16b8_2b64() {
+; CHECK-LABEL: @bitcast_constexpr_16b8_2b64(
+; CHECK-NEXT:    ret <2 x b64> bitcast (<16 x b8> splat (b8 2) to <2 x b64>)
+;
+  %res = bitcast <16 x b8> splat (b8 2) to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x i64> @bitcast_constexpr_4b32_2i64() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2i64(
+; CHECK-NEXT:    ret <2 x i64> bitcast (<4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x i64>)
+;
+  %res = bitcast <4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x i64>
+  ret <2 x i64> %res
+}
+
+define <4 x i32> @bitcast_constexpr_2b64_4i32() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_4i32(
+; CHECK-NEXT:    ret <4 x i32> bitcast (<2 x b64> <b64 0, b64 1> to <4 x i32>)
+;
+  %res = bitcast <2 x b64> <b64 0, b64 1> to <4 x i32>
+  ret <4 x i32> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4b32_2b64() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2b64(
+; CHECK-NEXT:    ret <2 x b64> bitcast (<4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x b64>)
+;
+  %res = bitcast <4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <16 x b8> @bitcast_constexpr_2b64_16b8() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_16b8(
+; CHECK-NEXT:    ret <16 x b8> bitcast (<2 x b64> <b64 0, b64 1> to <16 x b8>)
+;
+  %res = bitcast <2 x b64> <b64 0, b64 1> to <16 x b8>
+  ret <16 x b8> %res
+}
+
+define <2 x i32> @bitcast_constexpr_scalar_b64_to_vector_2i32() {
+; CHECK-LABEL: @bitcast_constexpr_scalar_b64_to_vector_2i32(
+; CHECK-NEXT:    ret <2 x i32> bitcast (b64 1 to <2 x i32>)
+;
+  %res = bitcast b64 1 to <2 x i32>
+  ret <2 x i32> %res
+}
+
+define <4 x float> @bitcast_constexpr_2b64_4f32() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_4f32(
+; CHECK-NEXT:    ret <4 x float> bitcast (<2 x b64> <b64 0, b64 1> to <4 x float>)
+;
+  %res = bitcast <2 x b64> <b64 0, b64 1> to <4 x float>
+  ret <4 x float> %res
+}
+
+define <2 x i64> @bitcast_constexpr_allones_4b32_2i64() {
+; CHECK-LABEL: @bitcast_constexpr_allones_4b32_2i64(
+; CHECK-NEXT:    ret <2 x i64> splat (i64 -1)
+;
+  %res = bitcast <4 x b32> splat (b32 -1) to <2 x i64>
+  ret <2 x i64> %res
+}
+
+define <4 x i32> @bitcast_constexpr_allones_4b32_4i32() {
+; CHECK-LABEL: @bitcast_constexpr_allones_4b32_4i32(
+; CHECK-NEXT:    ret <4 x i32> splat (i32 -1)
+;
+  %res = bitcast <4 x b32> splat (b32 -1) to <4 x i32>
+  ret <4 x i32> %res
+}

>From 24bfd4c8bf1cbd89849ffd8ceba52096e04669e5 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Wed, 2 Apr 2025 13:13:06 +0100
Subject: [PATCH 02/11] [ConstantFold] Support byte values in `bitcast`
 constant folding

Add support for constant folding `bitcast` instructions including
`ConstantByte` values. This patch handles bitcasts between byte types
and integer, FP, and other byte types in both directions.

In ConstantFolding.cpp's `FoldBitCast`, vector bitcasts types are folded
through integer vectors, as the function recurses on vector types. This
allows replacing the existing integer/FP vector checks with scalar type
checks.
---
 llvm/lib/Analysis/ConstantFolding.cpp         | 29 +++++++-
 llvm/lib/IR/ConstantFold.cpp                  | 56 +++++++++++----
 .../InstSimplify/bitcast-vector-fold.ll       | 70 +++++++++++++++++--
 3 files changed, 134 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 9dbab56348411..8d7c7919985ba 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -153,7 +153,7 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
   // If this is a scalar -> vector cast, convert the input into a <1 x scalar>
   // vector so the code below can handle it uniformly.
   if (!isa<VectorType>(C->getType()) &&
-      (isa<ConstantFP>(C) || isa<ConstantInt>(C))) {
+      (isa<ConstantFP>(C) || isa<ConstantInt>(C) || isa<ConstantByte>(C))) {
     Constant *Ops = C; // don't take the address of C!
     return FoldBitCast(ConstantVector::get(Ops), DestTy, DL);
   }
@@ -165,7 +165,7 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
 
   // If this is a bitcast from constant vector -> vector, fold it.
   if (!isa<ConstantDataVector>(C) && !isa<ConstantVector>(C) &&
-      !isa<ConstantInt>(C) && !isa<ConstantFP>(C))
+      !isa<ConstantInt>(C) && !isa<ConstantFP>(C) && !isa<ConstantByte>(C))
     return ConstantExpr::getBitCast(C, DestTy);
 
   // If the element types match, IR can fold it.
@@ -199,6 +199,19 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
     return ConstantExpr::getBitCast(C, DestTy);
   }
 
+  // Handle byte destination type by folding through integers.
+  if (DstEltTy->isByteTy()) {
+    // Fold to a vector of integers with same size as the byte type.
+    unsigned ByteWidth = DstEltTy->getPrimitiveSizeInBits();
+    auto *DestIVTy = FixedVectorType::get(
+        IntegerType::get(C->getContext(), ByteWidth), NumDstElt);
+    // Recursively handle this integer conversion, if possible.
+    C = FoldBitCast(C, DestIVTy, DL);
+
+    // Finally, IR can handle this now that #elts line up.
+    return ConstantExpr::getBitCast(C, DestTy);
+  }
+
   // Okay, we know the destination is integer, if the input is FP, convert
   // it to integer first.
   if (SrcEltTy->isFloatingPointTy()) {
@@ -212,6 +225,18 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
            "Constant folding cannot fail for plain fp->int bitcast!");
   }
 
+  // Handle byte source type by folding through integers.
+  if (SrcEltTy->isByteTy()) {
+    unsigned ByteWidth = SrcEltTy->getPrimitiveSizeInBits();
+    auto *SrcIVTy = FixedVectorType::get(
+        IntegerType::get(C->getContext(), ByteWidth), NumSrcElt);
+    // Ask IR to do the conversion now that #elts line up.
+    C = ConstantExpr::getBitCast(C, SrcIVTy);
+    assert((isa<ConstantVector>(C) || // FIXME: Remove ConstantVector.
+            isa<ConstantDataVector>(C) || isa<ConstantInt>(C)) &&
+           "Constant folding cannot fail for plain byte->int bitcast!");
+  }
+
   // Now we know that the input and output vectors are both integer vectors
   // of the same size, and that their #elements is not the same.  Do the
   // conversion here, which depends on whether the input or output has
diff --git a/llvm/lib/IR/ConstantFold.cpp b/llvm/lib/IR/ConstantFold.cpp
index 87a70391fbec4..a4a2d362b5d5b 100644
--- a/llvm/lib/IR/ConstantFold.cpp
+++ b/llvm/lib/IR/ConstantFold.cpp
@@ -68,7 +68,7 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
   if (V->isAllOnesValue())
     return Constant::getAllOnesValue(DestTy);
 
-  // Handle ConstantInt -> ConstantFP
+  // Handle ConstantInt -> Constant{Byte, FP}
   if (ConstantInt *CI = dyn_cast<ConstantInt>(V)) {
     // Canonicalize scalar-to-vector bitcasts into vector-to-vector bitcasts
     // This allows for other simplifications (although some of them
@@ -76,18 +76,45 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
     if (isa<VectorType>(DestTy) && !isa<VectorType>(SrcTy))
       return ConstantExpr::getBitCast(ConstantVector::get(V), DestTy);
 
+    if (DestTy->isByteTy() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantByte::get(DestTy->getContext(), CI->getValue());
+
     // Make sure dest type is compatible with the folded fp constant.
     // See note below regarding the PPC_FP128 restriction.
-    if (!DestTy->isFPOrFPVectorTy() || DestTy->isPPC_FP128Ty() ||
-        DestTy->getScalarSizeInBits() != SrcTy->getScalarSizeInBits())
-      return nullptr;
+    if (DestTy->isFloatingPointTy() && !DestTy->isPPC_FP128Ty() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantFP::get(
+          DestTy,
+          APFloat(DestTy->getScalarType()->getFltSemantics(), CI->getValue()));
 
-    return ConstantFP::get(
-        DestTy,
-        APFloat(DestTy->getScalarType()->getFltSemantics(), CI->getValue()));
+    return nullptr;
   }
 
-  // Handle ConstantFP -> Constant{Int, FP}
+  // Handle ConstantByte -> Constant{Int, FP}
+  if (ConstantByte *CB = dyn_cast<ConstantByte>(V)) {
+    // Canonicalize scalar-to-vector bitcasts into vector-to-vector bitcasts
+    // This allows for other simplifications (although some of them
+    // can only be handled by Analysis/ConstantFolding.cpp).
+    if (isa<VectorType>(DestTy) && !isa<VectorType>(SrcTy))
+      return ConstantExpr::getBitCast(ConstantVector::get(V), DestTy);
+
+    if (DestTy->isIntegerTy() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantInt::get(DestTy->getContext(), CB->getValue());
+
+    // Make sure dest type is compatible with the folded fp constant.
+    // See note below regarding the PPC_FP128 restriction.
+    if (DestTy->isFloatingPointTy() && !DestTy->isPPC_FP128Ty() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantFP::get(
+          DestTy,
+          APFloat(DestTy->getScalarType()->getFltSemantics(), CB->getValue()));
+
+    return nullptr;
+  }
+
+  // Handle ConstantFP -> Constant{Int, Byte, FP}
   if (ConstantFP *FP = dyn_cast<ConstantFP>(V)) {
     // Handle half <-> bfloat
     if (!isa<VectorType>(SrcTy) && DestTy->isFloatingPointTy()) {
@@ -111,11 +138,16 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
       return nullptr;
 
     // Make sure dest type is compatible with the folded integer constant.
-    if (!DestTy->isIntOrIntVectorTy() ||
-        DestTy->getScalarSizeInBits() != SrcTy->getScalarSizeInBits())
-      return nullptr;
+    if (DestTy->isIntOrIntVectorTy() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantInt::get(DestTy, FP->getValueAPF().bitcastToAPInt());
 
-    return ConstantInt::get(DestTy, FP->getValueAPF().bitcastToAPInt());
+    // Make sure dest type is compatible with the folded byte constant.
+    if (DestTy->isByteTy() &&
+        DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
+      return ConstantByte::get(DestTy, FP->getValueAPF().bitcastToAPInt());
+
+    return nullptr;
   }
 
   return nullptr;
diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index 65bfb61930344..1c08a65ca3090 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -293,9 +293,17 @@ define <2 x i64> @bitcast_constexpr_4f32_2i64_1111() {
   ret <2 x i64> %res
 }
 
+define <2 x b64> @bitcast_constexpr_16i8_2b64() {
+; CHECK-LABEL: @bitcast_constexpr_16i8_2b64(
+; CHECK-NEXT:    ret <2 x b64> splat (b64 144680345676153346)
+;
+  %res = bitcast <16 x i8> splat (i8 2) to <2 x b64>
+  ret <2 x b64> %res
+}
+
 define <2 x b64> @bitcast_constexpr_16b8_2b64() {
 ; CHECK-LABEL: @bitcast_constexpr_16b8_2b64(
-; CHECK-NEXT:    ret <2 x b64> bitcast (<16 x b8> splat (b8 2) to <2 x b64>)
+; CHECK-NEXT:    ret <2 x b64> splat (b64 144680345676153346)
 ;
   %res = bitcast <16 x b8> splat (b8 2) to <2 x b64>
   ret <2 x b64> %res
@@ -303,7 +311,7 @@ define <2 x b64> @bitcast_constexpr_16b8_2b64() {
 
 define <2 x i64> @bitcast_constexpr_4b32_2i64() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2i64(
-; CHECK-NEXT:    ret <2 x i64> bitcast (<4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x i64>)
+; CHECK-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
 ;
   %res = bitcast <4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x i64>
   ret <2 x i64> %res
@@ -311,15 +319,31 @@ define <2 x i64> @bitcast_constexpr_4b32_2i64() {
 
 define <4 x i32> @bitcast_constexpr_2b64_4i32() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_4i32(
-; CHECK-NEXT:    ret <4 x i32> bitcast (<2 x b64> <b64 0, b64 1> to <4 x i32>)
+; CHECK-NEXT:    ret <4 x i32> <i32 0, i32 0, i32 1, i32 0>
 ;
   %res = bitcast <2 x b64> <b64 0, b64 1> to <4 x i32>
   ret <4 x i32> %res
 }
 
+define <2 x b64> @bitcast_constexpr_4i32_2b64() {
+; CHECK-LABEL: @bitcast_constexpr_4i32_2b64(
+; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+;
+  %res = bitcast <4 x i32> <i32 0, i32 1, i32 2, i32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <4 x b32> @bitcast_constexpr_2i64_4b32() {
+; CHECK-LABEL: @bitcast_constexpr_2i64_4b32(
+; CHECK-NEXT:    ret <4 x b32> <b32 0, b32 0, b32 1, b32 0>
+;
+  %res = bitcast <2 x i64> <i64 0, i64 1> to <4 x b32>
+  ret <4 x b32> %res
+}
+
 define <2 x b64> @bitcast_constexpr_4b32_2b64() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2b64(
-; CHECK-NEXT:    ret <2 x b64> bitcast (<4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x b64>)
+; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
 ;
   %res = bitcast <4 x b32> <b32 0, b32 1, b32 2, b32 3> to <2 x b64>
   ret <2 x b64> %res
@@ -327,7 +351,7 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64() {
 
 define <16 x b8> @bitcast_constexpr_2b64_16b8() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_16b8(
-; CHECK-NEXT:    ret <16 x b8> bitcast (<2 x b64> <b64 0, b64 1> to <16 x b8>)
+; CHECK-NEXT:    ret <16 x b8> <b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 1, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0>
 ;
   %res = bitcast <2 x b64> <b64 0, b64 1> to <16 x b8>
   ret <16 x b8> %res
@@ -335,15 +359,31 @@ define <16 x b8> @bitcast_constexpr_2b64_16b8() {
 
 define <2 x i32> @bitcast_constexpr_scalar_b64_to_vector_2i32() {
 ; CHECK-LABEL: @bitcast_constexpr_scalar_b64_to_vector_2i32(
-; CHECK-NEXT:    ret <2 x i32> bitcast (b64 1 to <2 x i32>)
+; CHECK-NEXT:    ret <2 x i32> <i32 1, i32 0>
 ;
   %res = bitcast b64 1 to <2 x i32>
   ret <2 x i32> %res
 }
 
+define <2 x b32> @bitcast_constexpr_scalar_i64_to_vector_2b32() {
+; CHECK-LABEL: @bitcast_constexpr_scalar_i64_to_vector_2b32(
+; CHECK-NEXT:    ret <2 x b32> <b32 1, b32 0>
+;
+  %res = bitcast i64 1 to <2 x b32>
+  ret <2 x b32> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4f32_2b64() {
+; CHECK-LABEL: @bitcast_constexpr_4f32_2b64(
+; CHECK-NEXT:    ret <2 x b64> splat (b64 4575657222473777152)
+;
+  %res = bitcast <4 x float> splat (float 1.0) to <2 x b64>
+  ret <2 x b64> %res
+}
+
 define <4 x float> @bitcast_constexpr_2b64_4f32() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_4f32(
-; CHECK-NEXT:    ret <4 x float> bitcast (<2 x b64> <b64 0, b64 1> to <4 x float>)
+; CHECK-NEXT:    ret <4 x float> <float 0.000000e+00, float 0.000000e+00, float 0x36A0000000000000, float 0.000000e+00>
 ;
   %res = bitcast <2 x b64> <b64 0, b64 1> to <4 x float>
   ret <4 x float> %res
@@ -364,3 +404,19 @@ define <4 x i32> @bitcast_constexpr_allones_4b32_4i32() {
   %res = bitcast <4 x b32> splat (b32 -1) to <4 x i32>
   ret <4 x i32> %res
 }
+
+define <4 x b32> @bitcast_constexpr_allones_2i64_4b32() {
+; CHECK-LABEL: @bitcast_constexpr_allones_2i64_4b32(
+; CHECK-NEXT:    ret <4 x b32> splat (b32 -1)
+;
+  %res = bitcast <2 x i64> splat (i64 -1) to <4 x b32>
+  ret <4 x b32> %res
+}
+
+define <4 x b32> @bitcast_constexpr_allones_4i32_4b32() {
+; CHECK-LABEL: @bitcast_constexpr_allones_4i32_4b32(
+; CHECK-NEXT:    ret <4 x b32> splat (b32 -1)
+;
+  %res = bitcast <4 x i32> splat (i32 -1) to <4 x b32>
+  ret <4 x b32> %res
+}

>From 346e9981a5ce49928f0897260e5f8d8a9f1a619c Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 4 Apr 2026 12:57:09 +0100
Subject: [PATCH 03/11] Poison pre-commit tests

---
 .../InstSimplify/bitcast-vector-fold.ll       | 64 +++++++++++++++++++
 1 file changed, 64 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index 1c08a65ca3090..c11fbf6a6b17f 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -420,3 +420,67 @@ define <4 x b32> @bitcast_constexpr_allones_4i32_4b32() {
   %res = bitcast <4 x i32> splat (i32 -1) to <4 x b32>
   ret <4 x b32> %res
 }
+
+define <2 x b64> @bitcast_constexpr_4b32_2b64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_poison(
+; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+;
+  %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4i32_2b64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4i32_2b64_poison(
+; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901888>
+;
+  %res = bitcast <4 x i32> <i32 0, i32 1, i32 poison, i32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4f32_2b64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4f32_2b64_poison(
+; CHECK-NEXT:    ret <2 x b64> <b64 1065353216, b64 4629700418010611712>
+;
+  %res = bitcast <4 x float> <float 1.0, float poison, float 2.0, float 3.0> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x b64> @bitcast_constexpr_8b16_2b64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_8b16_2b64_poison(
+; CHECK-NEXT:    ret <2 x b64> <b64 8590000128, b64 1970350607106052>
+;
+  %res = bitcast <8 x b16> <b16 0, b16 1, b16 2, b16 poison, b16 4, b16 5, b16 6, b16 7> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison_group() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_all_poison_group(
+; CHECK-NEXT:    ret <2 x b64> <b64 0, b64 12884901890>
+;
+  %res = bitcast <4 x b32> <b32 poison, b32 poison, b32 2, b32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
+define <2 x i64> @bitcast_constexpr_4b32_2i64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2i64_poison(
+; CHECK-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
+;
+  %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x i64>
+  ret <2 x i64> %res
+}
+
+define <4 x b32> @bitcast_constexpr_2b64_4b32_poison() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_4b32_poison(
+; CHECK-NEXT:    ret <4 x b32> <b32 undef, b32 undef, b32 1, b32 0>
+;
+  %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x b32>
+  ret <4 x b32> %res
+}
+
+define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_all_poison(
+; CHECK-NEXT:    ret <2 x b64> poison
+;
+  %res = bitcast <4 x b32> splat (b32 poison) to <2 x b64>
+  ret <2 x b64> %res
+}

>From 3e6c9f4373761771e328e5eaba78e2479ac9f83f Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 4 Apr 2026 13:00:06 +0100
Subject: [PATCH 04/11] [ConstantFold] Don't fold byte vector bitcasts mixing
 `poison` bits

Byte types track `poison` on a per-bit basis, but no `ConstantByte`
value can represent mixed `poison` and non-`poison` bits. When folding a
bitcast that combines smaller elements into a larger byte element (e.g.,
`<4 x b32>` to `<2 x b64>`), bail out if any output lane contains both
`poison` and non-`poison` source elements.
---
 llvm/lib/Analysis/ConstantFolding.cpp         | 35 +++++++++++++++++++
 .../InstSimplify/bitcast-vector-fold.ll       |  8 ++---
 2 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 8d7c7919985ba..f389861fb1b87 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -105,6 +105,35 @@ static Constant *foldConstVectorToAPInt(APInt &Result, Type *DestTy,
   return nullptr;
 }
 
+/// Check whether folding this bitcast into a byte vector would mix poison and
+/// non-poison bits in the same output lane. While integer types track poison on
+/// a per-value basis, byte types track it on a per-bit basis. However,
+/// `ConstantByte` cannot represent values with both poison and non-poison bits.
+///
+/// Source elements are grouped by the output lane they map to. Returns true if
+/// any group contains both poison and non-poison elements.
+static bool foldMixesPoisonBits(Constant *C, unsigned NumSrcElt,
+                                unsigned NumDstElt) {
+  unsigned Ratio = NumSrcElt / NumDstElt;
+  for (unsigned i = 0; i != NumSrcElt; i += Ratio) {
+    bool HasPoison = false;
+    bool HasNonPoison = false;
+    for (unsigned j = 0; j != Ratio; ++j) {
+      Constant *Src = C->getAggregateElement(i + j);
+      // Conservatively bail out.
+      if (!Src)
+        return true;
+      if (isa<PoisonValue>(Src))
+        HasPoison = true;
+      else
+        HasNonPoison = true;
+    }
+    if (HasPoison && HasNonPoison)
+      return true;
+  }
+  return false;
+}
+
 /// Constant fold bitcast, symbolically evaluating it with DataLayout.
 /// This always returns a non-null constant, but it may be a
 /// ConstantExpr if unfoldable.
@@ -201,6 +230,12 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
 
   // Handle byte destination type by folding through integers.
   if (DstEltTy->isByteTy()) {
+    // When combining elements into larger byte values, bail out if the fold
+    // mixes poison and non-poison bits in the same destination element. Byte
+    // types track poison per bit, and no constant value can represent that.
+    if (NumDstElt < NumSrcElt && foldMixesPoisonBits(C, NumSrcElt, NumDstElt))
+      return ConstantExpr::getBitCast(C, DestTy);
+
     // Fold to a vector of integers with same size as the byte type.
     unsigned ByteWidth = DstEltTy->getPrimitiveSizeInBits();
     auto *DestIVTy = FixedVectorType::get(
diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index c11fbf6a6b17f..c506e857cab89 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -423,7 +423,7 @@ define <4 x b32> @bitcast_constexpr_allones_4i32_4b32() {
 
 define <2 x b64> @bitcast_constexpr_4b32_2b64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_poison(
-; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+; CHECK-NEXT:    ret <2 x b64> bitcast (<4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x b64>)
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x b64>
   ret <2 x b64> %res
@@ -431,7 +431,7 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64_poison() {
 
 define <2 x b64> @bitcast_constexpr_4i32_2b64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4i32_2b64_poison(
-; CHECK-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901888>
+; CHECK-NEXT:    ret <2 x b64> bitcast (<4 x i32> <i32 0, i32 1, i32 poison, i32 3> to <2 x b64>)
 ;
   %res = bitcast <4 x i32> <i32 0, i32 1, i32 poison, i32 3> to <2 x b64>
   ret <2 x b64> %res
@@ -439,7 +439,7 @@ define <2 x b64> @bitcast_constexpr_4i32_2b64_poison() {
 
 define <2 x b64> @bitcast_constexpr_4f32_2b64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4f32_2b64_poison(
-; CHECK-NEXT:    ret <2 x b64> <b64 1065353216, b64 4629700418010611712>
+; CHECK-NEXT:    ret <2 x b64> bitcast (<4 x float> <float 1.000000e+00, float poison, float 2.000000e+00, float 3.000000e+00> to <2 x b64>)
 ;
   %res = bitcast <4 x float> <float 1.0, float poison, float 2.0, float 3.0> to <2 x b64>
   ret <2 x b64> %res
@@ -447,7 +447,7 @@ define <2 x b64> @bitcast_constexpr_4f32_2b64_poison() {
 
 define <2 x b64> @bitcast_constexpr_8b16_2b64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_8b16_2b64_poison(
-; CHECK-NEXT:    ret <2 x b64> <b64 8590000128, b64 1970350607106052>
+; CHECK-NEXT:    ret <2 x b64> bitcast (<8 x b16> <b16 0, b16 1, b16 2, b16 poison, b16 4, b16 5, b16 6, b16 7> to <2 x b64>)
 ;
   %res = bitcast <8 x b16> <b16 0, b16 1, b16 2, b16 poison, b16 4, b16 5, b16 6, b16 7> to <2 x b64>
   ret <2 x b64> %res

>From 4443f9d66e36147f1becfe9b0aeb4016e159ee21 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sun, 5 Apr 2026 19:40:46 +0100
Subject: [PATCH 05/11] Another poison pre-commit test

---
 llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index c506e857cab89..c4ebc02cefd9d 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -461,6 +461,14 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison_group() {
   ret <2 x b64> %res
 }
 
+define <2 x b64> @bitcast_constexpr_4i32_2b64_all_poison_group() {
+; CHECK-LABEL: @bitcast_constexpr_4i32_2b64_all_poison_group(
+; CHECK-NEXT:    ret <2 x b64> <b64 0, b64 12884901890>
+;
+  %res = bitcast <4 x i32> <i32 poison, i32 poison, i32 2, i32 3> to <2 x b64>
+  ret <2 x b64> %res
+}
+
 define <2 x i64> @bitcast_constexpr_4b32_2i64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2i64_poison(
 ; CHECK-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>

>From 29de94c165859f1d1ff11371e5c2a966530bc524 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sun, 5 Apr 2026 19:24:49 +0100
Subject: [PATCH 06/11] [ConstantFold] Preserve `poison` in byte vector
 `bitcast` folding

Bitcasts to vector of the byte type are folded through integer vectors.
This introduces undesirable refinements of `poison` to `undef`/zero.
Track which destination lanes are fully `poison` before the integer
fold, and restore those lanes afterward.
---
 llvm/lib/Analysis/ConstantFolding.cpp         | 50 ++++++++++++++++++-
 .../InstSimplify/bitcast-vector-fold.ll       |  6 +--
 2 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index f389861fb1b87..06598f43fca0c 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -22,6 +22,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/DenseMap.h"
 #include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Analysis/TargetFolder.h"
@@ -236,6 +237,37 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
     if (NumDstElt < NumSrcElt && foldMixesPoisonBits(C, NumSrcElt, NumDstElt))
       return ConstantExpr::getBitCast(C, DestTy);
 
+    // The recursive call folds poison to undef/zero.
+    // Track which destination lanes are poison, so these can be restored.
+    Constant *OrigC = C;
+    SmallBitVector PoisonDstElts(NumDstElt);
+    if (NumDstElt < NumSrcElt) {
+      // A destination lane is poison iff all source elements in its group are
+      // poison. Mixed groups were already rejected above.
+      unsigned Ratio = NumSrcElt / NumDstElt;
+      for (unsigned i = 0; i != NumDstElt; ++i) {
+        bool AllPoison = true;
+        for (unsigned j = 0; j != Ratio; ++j) {
+          Constant *Src = C->getAggregateElement(i * Ratio + j);
+          if (!Src)
+            return ConstantExpr::getBitCast(OrigC, DestTy);
+          if (!isa<PoisonValue>(Src))
+            AllPoison = false;
+        }
+        PoisonDstElts[i] = AllPoison;
+      }
+    } else if (NumDstElt > NumSrcElt) {
+      // Destination elements are poison iff their source element is poison.
+      unsigned Ratio = NumDstElt / NumSrcElt;
+      for (unsigned i = 0; i != NumSrcElt; ++i) {
+        Constant *Src = C->getAggregateElement(i);
+        if (!Src)
+          return ConstantExpr::getBitCast(OrigC, DestTy);
+        if (isa<PoisonValue>(Src))
+          PoisonDstElts.set(i * Ratio, (i + 1) * Ratio);
+      }
+    }
+
     // Fold to a vector of integers with same size as the byte type.
     unsigned ByteWidth = DstEltTy->getPrimitiveSizeInBits();
     auto *DestIVTy = FixedVectorType::get(
@@ -243,8 +275,22 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
     // Recursively handle this integer conversion, if possible.
     C = FoldBitCast(C, DestIVTy, DL);
 
-    // Finally, IR can handle this now that #elts line up.
-    return ConstantExpr::getBitCast(C, DestTy);
+    if (PoisonDstElts.none())
+      return ConstantExpr::getBitCast(C, DestTy);
+
+    // Restore poison lanes that the integer fold refined to undef/zero.
+    SmallVector<Constant *, 32> Elts;
+    for (unsigned i = 0; i != NumDstElt; ++i) {
+      if (PoisonDstElts[i]) {
+        Elts.push_back(PoisonValue::get(DstEltTy));
+      } else {
+        Constant *E = C->getAggregateElement(i);
+        if (!E)
+          return ConstantExpr::getBitCast(OrigC, DestTy);
+        Elts.push_back(ConstantExpr::getBitCast(E, DstEltTy));
+      }
+    }
+    return ConstantVector::get(Elts);
   }
 
   // Okay, we know the destination is integer, if the input is FP, convert
diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index c4ebc02cefd9d..edb7fab52ce2a 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -455,7 +455,7 @@ define <2 x b64> @bitcast_constexpr_8b16_2b64_poison() {
 
 define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison_group() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_all_poison_group(
-; CHECK-NEXT:    ret <2 x b64> <b64 0, b64 12884901890>
+; CHECK-NEXT:    ret <2 x b64> <b64 poison, b64 12884901890>
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 poison, b32 2, b32 3> to <2 x b64>
   ret <2 x b64> %res
@@ -463,7 +463,7 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison_group() {
 
 define <2 x b64> @bitcast_constexpr_4i32_2b64_all_poison_group() {
 ; CHECK-LABEL: @bitcast_constexpr_4i32_2b64_all_poison_group(
-; CHECK-NEXT:    ret <2 x b64> <b64 0, b64 12884901890>
+; CHECK-NEXT:    ret <2 x b64> <b64 poison, b64 12884901890>
 ;
   %res = bitcast <4 x i32> <i32 poison, i32 poison, i32 2, i32 3> to <2 x b64>
   ret <2 x b64> %res
@@ -479,7 +479,7 @@ define <2 x i64> @bitcast_constexpr_4b32_2i64_poison() {
 
 define <4 x b32> @bitcast_constexpr_2b64_4b32_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_4b32_poison(
-; CHECK-NEXT:    ret <4 x b32> <b32 undef, b32 undef, b32 1, b32 0>
+; CHECK-NEXT:    ret <4 x b32> <b32 poison, b32 poison, b32 1, b32 0>
 ;
   %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x b32>
   ret <4 x b32> %res

>From 24fd1c240ddf6fb0fb17466308070c3ff446174b Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 12:08:15 +0100
Subject: [PATCH 07/11] More pre-commit tests

---
 .../InstSimplify/bitcast-vector-fold.ll       | 24 +++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index edb7fab52ce2a..55d977b707688 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -492,3 +492,27 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison() {
   %res = bitcast <4 x b32> splat (b32 poison) to <2 x b64>
   ret <2 x b64> %res
 }
+
+define <4 x i32> @bitcast_constexpr_2b64_4i32_poison() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_4i32_poison(
+; CHECK-NEXT:    ret <4 x i32> <i32 undef, i32 undef, i32 1, i32 0>
+;
+  %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x i32>
+  ret <4 x i32> %res
+}
+
+define <2 x double> @bitcast_constexpr_4b32_2f64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_4b32_2f64_poison(
+; CHECK-NEXT:    ret <2 x double> <double 0x100000000, double 0x300000002>
+;
+  %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x double>
+  ret <2 x double> %res
+}
+
+define <4 x float> @bitcast_constexpr_2b64_4f32_poison() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_4f32_poison(
+; CHECK-NEXT:    ret <4 x float> <float undef, float undef, float 0x36A0000000000000, float 0.000000e+00>
+;
+  %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x float>
+  ret <4 x float> %res
+}

>From 260955b5d63021c753d3dd4774d8b0093164fd2e Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 12:15:17 +0100
Subject: [PATCH 08/11] [ConstantFold] Restore `poison` when bitcasting from
 bytes

Bitcasts from byte vectors to integer or floating-point destinations are
folded through integer vectors, which refines `poison` bytes to `undef`
or zero. Although this is a valid refinement, it is undesirable. Track
which destination lanes fold to `poison` (and are refined to `undef` or
zero) and restore them after the fold.
---
 llvm/lib/Analysis/ConstantFolding.cpp         | 90 +++++++++++++------
 .../InstSimplify/bitcast-vector-fold.ll       |  8 +-
 2 files changed, 66 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 06598f43fca0c..ed31dfbdefb52 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -306,8 +306,37 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
            "Constant folding cannot fail for plain fp->int bitcast!");
   }
 
-  // Handle byte source type by folding through integers.
+  // Handle byte source type by folding through integers. Byte types track
+  // poison per bit, so any poison bit makes the destination lane poison.
+  // Record which destination lanes contain poison bits, before the generic
+  // fold below refines them to undef/zero, so they can be restored.
+  SmallBitVector PoisonDstElts(NumDstElt);
   if (SrcEltTy->isByteTy()) {
+    Constant *OrigC = C;
+    if (NumDstElt < NumSrcElt) {
+      unsigned Ratio = NumSrcElt / NumDstElt;
+      for (unsigned i = 0; i != NumDstElt; ++i) {
+        for (unsigned j = 0; j != Ratio; ++j) {
+          Constant *Src = C->getAggregateElement(i * Ratio + j);
+          if (!Src)
+            return ConstantExpr::getBitCast(OrigC, DestTy);
+          if (isa<PoisonValue>(Src)) {
+            PoisonDstElts[i] = true;
+            break;
+          }
+        }
+      }
+    } else {
+      unsigned Ratio = NumDstElt / NumSrcElt;
+      for (unsigned i = 0; i != NumSrcElt; ++i) {
+        Constant *Src = C->getAggregateElement(i);
+        if (!Src)
+          return ConstantExpr::getBitCast(OrigC, DestTy);
+        if (isa<PoisonValue>(Src))
+          PoisonDstElts.set(i * Ratio, (i + 1) * Ratio);
+      }
+    }
+
     unsigned ByteWidth = SrcEltTy->getPrimitiveSizeInBits();
     auto *SrcIVTy = FixedVectorType::get(
         IntegerType::get(C->getContext(), ByteWidth), NumSrcElt);
@@ -364,42 +393,47 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
       }
       Result.push_back(Elt);
     }
-    return ConstantVector::get(Result);
-  }
-
-  // Handle: bitcast (<2 x i64> <i64 0, i64 1> to <4 x i32>)
-  unsigned Ratio = NumDstElt/NumSrcElt;
-  unsigned DstBitSize = DL.getTypeSizeInBits(DstEltTy);
+  } else {
+    // Handle: bitcast (<2 x i64> <i64 0, i64 1> to <4 x i32>)
+    unsigned Ratio = NumDstElt / NumSrcElt;
+    unsigned DstBitSize = DL.getTypeSizeInBits(DstEltTy);
 
-  // Loop over each source value, expanding into multiple results.
-  for (unsigned i = 0; i != NumSrcElt; ++i) {
-    auto *Element = C->getAggregateElement(i);
+    // Loop over each source value, expanding into multiple results.
+    for (unsigned i = 0; i != NumSrcElt; ++i) {
+      auto *Element = C->getAggregateElement(i);
 
-    if (!Element) // Reject constantexpr elements.
-      return ConstantExpr::getBitCast(C, DestTy);
+      if (!Element) // Reject constantexpr elements.
+        return ConstantExpr::getBitCast(C, DestTy);
 
-    if (isa<UndefValue>(Element)) {
-      // Correctly Propagate undef values.
-      Result.append(Ratio, UndefValue::get(DstEltTy));
-      continue;
-    }
+      if (isa<UndefValue>(Element)) {
+        // Correctly Propagate undef values.
+        Result.append(Ratio, UndefValue::get(DstEltTy));
+        continue;
+      }
 
-    auto *Src = dyn_cast<ConstantInt>(Element);
-    if (!Src)
-      return ConstantExpr::getBitCast(C, DestTy);
+      auto *Src = dyn_cast<ConstantInt>(Element);
+      if (!Src)
+        return ConstantExpr::getBitCast(C, DestTy);
 
-    unsigned ShiftAmt = isLittleEndian ? 0 : DstBitSize*(Ratio-1);
-    for (unsigned j = 0; j != Ratio; ++j) {
-      // Shift the piece of the value into the right place, depending on
-      // endianness.
-      APInt Elt = Src->getValue().lshr(ShiftAmt);
-      ShiftAmt += isLittleEndian ? DstBitSize : -DstBitSize;
+      unsigned ShiftAmt = isLittleEndian ? 0 : DstBitSize * (Ratio - 1);
+      for (unsigned j = 0; j != Ratio; ++j) {
+        // Shift the piece of the value into the right place, depending on
+        // endianness.
+        APInt Elt = Src->getValue().lshr(ShiftAmt);
+        ShiftAmt += isLittleEndian ? DstBitSize : -DstBitSize;
 
-      // Truncate and remember this piece.
-      Result.push_back(ConstantInt::get(DstEltTy, Elt.trunc(DstBitSize)));
+        // Truncate and remember this piece.
+        Result.push_back(ConstantInt::get(DstEltTy, Elt.trunc(DstBitSize)));
+      }
     }
   }
 
+  // Restore destination lanes whose source bytes contained poison bits.
+  if (PoisonDstElts.any())
+    for (unsigned i = 0; i != NumDstElt; ++i)
+      if (PoisonDstElts[i])
+        Result[i] = PoisonValue::get(DstEltTy);
+
   return ConstantVector::get(Result);
 }
 
diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index 55d977b707688..23d152fdca68a 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -471,7 +471,7 @@ define <2 x b64> @bitcast_constexpr_4i32_2b64_all_poison_group() {
 
 define <2 x i64> @bitcast_constexpr_4b32_2i64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2i64_poison(
-; CHECK-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
+; CHECK-NEXT:    ret <2 x i64> <i64 poison, i64 12884901890>
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x i64>
   ret <2 x i64> %res
@@ -495,7 +495,7 @@ define <2 x b64> @bitcast_constexpr_4b32_2b64_all_poison() {
 
 define <4 x i32> @bitcast_constexpr_2b64_4i32_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_4i32_poison(
-; CHECK-NEXT:    ret <4 x i32> <i32 undef, i32 undef, i32 1, i32 0>
+; CHECK-NEXT:    ret <4 x i32> <i32 poison, i32 poison, i32 1, i32 0>
 ;
   %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x i32>
   ret <4 x i32> %res
@@ -503,7 +503,7 @@ define <4 x i32> @bitcast_constexpr_2b64_4i32_poison() {
 
 define <2 x double> @bitcast_constexpr_4b32_2f64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_4b32_2f64_poison(
-; CHECK-NEXT:    ret <2 x double> <double 0x100000000, double 0x300000002>
+; CHECK-NEXT:    ret <2 x double> <double poison, double 0x300000002>
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x double>
   ret <2 x double> %res
@@ -511,7 +511,7 @@ define <2 x double> @bitcast_constexpr_4b32_2f64_poison() {
 
 define <4 x float> @bitcast_constexpr_2b64_4f32_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_4f32_poison(
-; CHECK-NEXT:    ret <4 x float> <float undef, float undef, float 0x36A0000000000000, float 0.000000e+00>
+; CHECK-NEXT:    ret <4 x float> <float poison, float poison, float 0x36A0000000000000, float 0.000000e+00>
 ;
   %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x float>
   ret <4 x float> %res

>From fd4d315ff7ce1a0dfe64674cd3dace61fd81ada2 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 12:17:31 +0100
Subject: [PATCH 09/11] Vector-to-scalar pre-commit tests

---
 .../InstSimplify/bitcast-vector-fold.ll       | 32 +++++++++++++++++++
 1 file changed, 32 insertions(+)

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index 23d152fdca68a..50497d26c6bf2 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -516,3 +516,35 @@ define <4 x float> @bitcast_constexpr_2b64_4f32_poison() {
   %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x float>
   ret <4 x float> %res
 }
+
+define i128 @bitcast_constexpr_2b64_i128() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_i128(
+; CHECK-NEXT:    ret i128 bitcast (<2 x b64> <b64 2, b64 1> to i128)
+;
+  %res = bitcast <2 x b64> <b64 2, b64 1> to i128
+  ret i128 %res
+}
+
+define i128 @bitcast_constexpr_2b64_i128_poison() {
+; CHECK-LABEL: @bitcast_constexpr_2b64_i128_poison(
+; CHECK-NEXT:    ret i128 bitcast (<2 x b64> <b64 poison, b64 1> to i128)
+;
+  %res = bitcast <2 x b64> <b64 poison, b64 1> to i128
+  ret i128 %res
+}
+
+define double @bitcast_constexpr_2b32_f64() {
+; CHECK-LABEL: @bitcast_constexpr_2b32_f64(
+; CHECK-NEXT:    ret double bitcast (<2 x b32> <b32 2, b32 1> to double)
+;
+  %res = bitcast <2 x b32> <b32 2, b32 1> to double
+  ret double %res
+}
+
+define double @bitcast_constexpr_2b32_f64_poison() {
+; CHECK-LABEL: @bitcast_constexpr_2b32_f64_poison(
+; CHECK-NEXT:    ret double bitcast (<2 x b32> <b32 poison, b32 1> to double)
+;
+  %res = bitcast <2 x b32> <b32 poison, b32 1> to double
+  ret double %res
+}

>From 3a9313e336e55a65586d50b2063cb6e93d5d6ec3 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 12:23:22 +0100
Subject: [PATCH 10/11] [ConstantFold] Fold bitcast from byte vector to scalar
 int/FP

Fold vector-to-scalar bitcasts from byte vectors. Any `poison` byte in
the source vector makes the scalar result `poison`.
---
 llvm/lib/Analysis/ConstantFolding.cpp             | 15 ++++++++++-----
 .../InstSimplify/bitcast-vector-fold.ll           |  8 ++++----
 2 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index ed31dfbdefb52..ab266f86ac0bf 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -152,12 +152,17 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
       unsigned NumSrcElts = cast<FixedVectorType>(VTy)->getNumElements();
       Type *SrcEltTy = VTy->getElementType();
 
-      // If the vector is a vector of floating point, convert it to vector of int
-      // to simplify things.
-      if (SrcEltTy->isFloatingPointTy()) {
-        unsigned FPWidth = SrcEltTy->getPrimitiveSizeInBits();
+      // Bitcasting a byte containing any poison bit to an integer or fp type
+      // yields poison.
+      if (SrcEltTy->isByteTy() && C->containsPoisonElement())
+        return PoisonValue::get(DestTy);
+
+      // If the vector is a vector of floating point or bytes, convert it to a
+      // vector of int to simplify things.
+      if (SrcEltTy->isFloatingPointTy() || SrcEltTy->isByteTy()) {
+        unsigned Width = SrcEltTy->getPrimitiveSizeInBits();
         auto *SrcIVTy = FixedVectorType::get(
-            IntegerType::get(C->getContext(), FPWidth), NumSrcElts);
+            IntegerType::get(C->getContext(), Width), NumSrcElts);
         // Ask IR to do the conversion now that #elts line up.
         C = ConstantExpr::getBitCast(C, SrcIVTy);
       }
diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index 50497d26c6bf2..de746d91a8f99 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -519,7 +519,7 @@ define <4 x float> @bitcast_constexpr_2b64_4f32_poison() {
 
 define i128 @bitcast_constexpr_2b64_i128() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_i128(
-; CHECK-NEXT:    ret i128 bitcast (<2 x b64> <b64 2, b64 1> to i128)
+; CHECK-NEXT:    ret i128 18446744073709551618
 ;
   %res = bitcast <2 x b64> <b64 2, b64 1> to i128
   ret i128 %res
@@ -527,7 +527,7 @@ define i128 @bitcast_constexpr_2b64_i128() {
 
 define i128 @bitcast_constexpr_2b64_i128_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_2b64_i128_poison(
-; CHECK-NEXT:    ret i128 bitcast (<2 x b64> <b64 poison, b64 1> to i128)
+; CHECK-NEXT:    ret i128 poison
 ;
   %res = bitcast <2 x b64> <b64 poison, b64 1> to i128
   ret i128 %res
@@ -535,7 +535,7 @@ define i128 @bitcast_constexpr_2b64_i128_poison() {
 
 define double @bitcast_constexpr_2b32_f64() {
 ; CHECK-LABEL: @bitcast_constexpr_2b32_f64(
-; CHECK-NEXT:    ret double bitcast (<2 x b32> <b32 2, b32 1> to double)
+; CHECK-NEXT:    ret double 0x100000002
 ;
   %res = bitcast <2 x b32> <b32 2, b32 1> to double
   ret double %res
@@ -543,7 +543,7 @@ define double @bitcast_constexpr_2b32_f64() {
 
 define double @bitcast_constexpr_2b32_f64_poison() {
 ; CHECK-LABEL: @bitcast_constexpr_2b32_f64_poison(
-; CHECK-NEXT:    ret double bitcast (<2 x b32> <b32 poison, b32 1> to double)
+; CHECK-NEXT:    ret double poison
 ;
   %res = bitcast <2 x b32> <b32 poison, b32 1> to double
   ret double %res

>From b7266175ff9e9713737149d668025849e433ed0a Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 12:57:37 +0100
Subject: [PATCH 11/11] refactor: factor out poison-lane computation

---
 llvm/lib/Analysis/ConstantFolding.cpp | 86 ++++++++++++---------------
 1 file changed, 37 insertions(+), 49 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index ab266f86ac0bf..3e247c6aa8061 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -135,6 +135,39 @@ static bool foldMixesPoisonBits(Constant *C, unsigned NumSrcElt,
   return false;
 }
 
+/// Track which destination lanes of a bitcast are produced from poison bytes.
+/// A destination lane is marked if any source element mapped to it is poison.
+/// Returns false if an aggregate element cannot be inspected. The caller should
+/// bail out of folding.
+static bool computePoisonDstLanes(Constant *C, unsigned NumSrcElt,
+                                  unsigned NumDstElt,
+                                  SmallBitVector &PoisonDstElts) {
+  if (NumDstElt < NumSrcElt) {
+    unsigned Ratio = NumSrcElt / NumDstElt;
+    for (unsigned i = 0; i != NumDstElt; ++i) {
+      for (unsigned j = 0; j != Ratio; ++j) {
+        Constant *Src = C->getAggregateElement(i * Ratio + j);
+        if (!Src)
+          return false;
+        if (isa<PoisonValue>(Src)) {
+          PoisonDstElts[i] = true;
+          break;
+        }
+      }
+    }
+  } else {
+    unsigned Ratio = NumDstElt / NumSrcElt;
+    for (unsigned i = 0; i != NumSrcElt; ++i) {
+      Constant *Src = C->getAggregateElement(i);
+      if (!Src)
+        return false;
+      if (isa<PoisonValue>(Src))
+        PoisonDstElts.set(i * Ratio, (i + 1) * Ratio);
+    }
+  }
+  return true;
+}
+
 /// Constant fold bitcast, symbolically evaluating it with DataLayout.
 /// This always returns a non-null constant, but it may be a
 /// ConstantExpr if unfoldable.
@@ -246,32 +279,8 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
     // Track which destination lanes are poison, so these can be restored.
     Constant *OrigC = C;
     SmallBitVector PoisonDstElts(NumDstElt);
-    if (NumDstElt < NumSrcElt) {
-      // A destination lane is poison iff all source elements in its group are
-      // poison. Mixed groups were already rejected above.
-      unsigned Ratio = NumSrcElt / NumDstElt;
-      for (unsigned i = 0; i != NumDstElt; ++i) {
-        bool AllPoison = true;
-        for (unsigned j = 0; j != Ratio; ++j) {
-          Constant *Src = C->getAggregateElement(i * Ratio + j);
-          if (!Src)
-            return ConstantExpr::getBitCast(OrigC, DestTy);
-          if (!isa<PoisonValue>(Src))
-            AllPoison = false;
-        }
-        PoisonDstElts[i] = AllPoison;
-      }
-    } else if (NumDstElt > NumSrcElt) {
-      // Destination elements are poison iff their source element is poison.
-      unsigned Ratio = NumDstElt / NumSrcElt;
-      for (unsigned i = 0; i != NumSrcElt; ++i) {
-        Constant *Src = C->getAggregateElement(i);
-        if (!Src)
-          return ConstantExpr::getBitCast(OrigC, DestTy);
-        if (isa<PoisonValue>(Src))
-          PoisonDstElts.set(i * Ratio, (i + 1) * Ratio);
-      }
-    }
+    if (!computePoisonDstLanes(C, NumSrcElt, NumDstElt, PoisonDstElts))
+      return ConstantExpr::getBitCast(OrigC, DestTy);
 
     // Fold to a vector of integers with same size as the byte type.
     unsigned ByteWidth = DstEltTy->getPrimitiveSizeInBits();
@@ -318,29 +327,8 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
   SmallBitVector PoisonDstElts(NumDstElt);
   if (SrcEltTy->isByteTy()) {
     Constant *OrigC = C;
-    if (NumDstElt < NumSrcElt) {
-      unsigned Ratio = NumSrcElt / NumDstElt;
-      for (unsigned i = 0; i != NumDstElt; ++i) {
-        for (unsigned j = 0; j != Ratio; ++j) {
-          Constant *Src = C->getAggregateElement(i * Ratio + j);
-          if (!Src)
-            return ConstantExpr::getBitCast(OrigC, DestTy);
-          if (isa<PoisonValue>(Src)) {
-            PoisonDstElts[i] = true;
-            break;
-          }
-        }
-      }
-    } else {
-      unsigned Ratio = NumDstElt / NumSrcElt;
-      for (unsigned i = 0; i != NumSrcElt; ++i) {
-        Constant *Src = C->getAggregateElement(i);
-        if (!Src)
-          return ConstantExpr::getBitCast(OrigC, DestTy);
-        if (isa<PoisonValue>(Src))
-          PoisonDstElts.set(i * Ratio, (i + 1) * Ratio);
-      }
-    }
+    if (!computePoisonDstLanes(C, NumSrcElt, NumDstElt, PoisonDstElts))
+      return ConstantExpr::getBitCast(OrigC, DestTy);
 
     unsigned ByteWidth = SrcEltTy->getPrimitiveSizeInBits();
     auto *SrcIVTy = FixedVectorType::get(



More information about the llvm-commits mailing list