[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 12:00:17 PDT 2026


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

>From 1ca72dcee8015fa7a875a4e3750ace41e199f47d 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/12] 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 be00d78fc88d5..beff470dde079 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -433,3 +433,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 599b188041e42e995d7252fa7044c5ab70eb72b6 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/12] [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       | 109 +++++++++++++++---
 3 files changed, 167 insertions(+), 27 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index cb8ecc3872c0c..7a1d6f5c343b4 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.
   // Use data buffer for easy non-integer element ratio vectors handling,
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 beff470dde079..d25802660cec2 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -434,57 +434,124 @@ 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
 }
 
 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>)
+; LE-LABEL: @bitcast_constexpr_4b32_2i64(
+; LE-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2i64(
+; BE-NEXT:    ret <2 x i64> <i64 1, i64 8589934595>
 ;
   %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>)
+; LE-LABEL: @bitcast_constexpr_2b64_4i32(
+; LE-NEXT:    ret <4 x i32> <i32 0, i32 0, i32 1, i32 0>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_4i32(
+; BE-NEXT:    ret <4 x i32> <i32 0, i32 0, i32 0, i32 1>
 ;
   %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() {
+; LE-LABEL: @bitcast_constexpr_4i32_2b64(
+; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4i32_2b64(
+; BE-NEXT:    ret <2 x b64> <b64 1, b64 8589934595>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_2i64_4b32(
+; LE-NEXT:    ret <4 x b32> <b32 0, b32 0, b32 1, b32 0>
+;
+; BE-LABEL: @bitcast_constexpr_2i64_4b32(
+; BE-NEXT:    ret <4 x b32> <b32 0, b32 0, b32 0, b32 1>
+;
+  %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>)
+; LE-LABEL: @bitcast_constexpr_4b32_2b64(
+; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2b64(
+; BE-NEXT:    ret <2 x b64> <b64 1, b64 8589934595>
 ;
   %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>)
+; LE-LABEL: @bitcast_constexpr_2b64_16b8(
+; LE-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>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_16b8(
+; BE-NEXT:    ret <16 x b8> <b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 0, b8 1>
 ;
   %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>)
+; LE-LABEL: @bitcast_constexpr_scalar_b64_to_vector_2i32(
+; LE-NEXT:    ret <2 x i32> <i32 1, i32 0>
+;
+; BE-LABEL: @bitcast_constexpr_scalar_b64_to_vector_2i32(
+; BE-NEXT:    ret <2 x i32> <i32 0, i32 1>
 ;
   %res = bitcast b64 1 to <2 x i32>
   ret <2 x i32> %res
 }
 
+define <2 x b32> @bitcast_constexpr_scalar_i64_to_vector_2b32() {
+; LE-LABEL: @bitcast_constexpr_scalar_i64_to_vector_2b32(
+; LE-NEXT:    ret <2 x b32> <b32 1, b32 0>
+;
+; BE-LABEL: @bitcast_constexpr_scalar_i64_to_vector_2b32(
+; BE-NEXT:    ret <2 x b32> <b32 0, b32 1>
+;
+  %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>)
+; LE-LABEL: @bitcast_constexpr_2b64_4f32(
+; LE-NEXT:    ret <4 x float> <float 0.000000e+00, float 0.000000e+00, float 0x36A0000000000000, float 0.000000e+00>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_4f32(
+; BE-NEXT:    ret <4 x float> <float 0.000000e+00, float 0.000000e+00, float 0.000000e+00, float 0x36A0000000000000>
 ;
   %res = bitcast <2 x b64> <b64 0, b64 1> to <4 x float>
   ret <4 x float> %res
@@ -505,3 +572,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 d837edaf12f57c227f472a6cf9f619402d8c6125 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/12] Poison pre-commit tests

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

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index d25802660cec2..30e97764d20c1 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -588,3 +588,88 @@ 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() {
+; LE-LABEL: @bitcast_constexpr_4b32_2b64_poison(
+; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2b64_poison(
+; BE-NEXT:    ret <2 x b64> <b64 1, b64 8589934595>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_4i32_2b64_poison(
+; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901888>
+;
+; BE-LABEL: @bitcast_constexpr_4i32_2b64_poison(
+; BE-NEXT:    ret <2 x b64> <b64 1, b64 3>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_4f32_2b64_poison(
+; LE-NEXT:    ret <2 x b64> <b64 1065353216, b64 4629700418010611712>
+;
+; BE-LABEL: @bitcast_constexpr_4f32_2b64_poison(
+; BE-NEXT:    ret <2 x b64> <b64 0, b64 4611686019505324032>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_8b16_2b64_poison(
+; LE-NEXT:    ret <2 x b64> <b64 8590000128, b64 1970350607106052>
+;
+; BE-LABEL: @bitcast_constexpr_8b16_2b64_poison(
+; BE-NEXT:    ret <2 x b64> <b64 0, b64 1125921382072327>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_4b32_2b64_all_poison_group(
+; LE-NEXT:    ret <2 x b64> <b64 poison, b64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2b64_all_poison_group(
+; BE-NEXT:    ret <2 x b64> <b64 poison, b64 8589934595>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_4b32_2i64_poison(
+; LE-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2i64_poison(
+; BE-NEXT:    ret <2 x i64> <i64 1, i64 8589934595>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_2b64_4b32_poison(
+; LE-NEXT:    ret <4 x b32> <b32 poison, b32 poison, b32 1, b32 0>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_4b32_poison(
+; BE-NEXT:    ret <4 x b32> <b32 poison, b32 poison, b32 0, b32 1>
+;
+  %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 6a9fe400466e72fbb8d2c803068d5387074ddc74 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/12] [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       | 28 +++++----------
 2 files changed, 43 insertions(+), 20 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 7a1d6f5c343b4..c881ca262b500 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 30e97764d20c1..cc1cf52119d83 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -590,44 +590,32 @@ define <4 x b32> @bitcast_constexpr_allones_4i32_4b32() {
 }
 
 define <2 x b64> @bitcast_constexpr_4b32_2b64_poison() {
-; LE-LABEL: @bitcast_constexpr_4b32_2b64_poison(
-; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901890>
-;
-; BE-LABEL: @bitcast_constexpr_4b32_2b64_poison(
-; BE-NEXT:    ret <2 x b64> <b64 1, b64 8589934595>
+; CHECK-LABEL: @bitcast_constexpr_4b32_2b64_poison(
+; 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
 }
 
 define <2 x b64> @bitcast_constexpr_4i32_2b64_poison() {
-; LE-LABEL: @bitcast_constexpr_4i32_2b64_poison(
-; LE-NEXT:    ret <2 x b64> <b64 4294967296, b64 12884901888>
-;
-; BE-LABEL: @bitcast_constexpr_4i32_2b64_poison(
-; BE-NEXT:    ret <2 x b64> <b64 1, b64 3>
+; CHECK-LABEL: @bitcast_constexpr_4i32_2b64_poison(
+; 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
 }
 
 define <2 x b64> @bitcast_constexpr_4f32_2b64_poison() {
-; LE-LABEL: @bitcast_constexpr_4f32_2b64_poison(
-; LE-NEXT:    ret <2 x b64> <b64 1065353216, b64 4629700418010611712>
-;
-; BE-LABEL: @bitcast_constexpr_4f32_2b64_poison(
-; BE-NEXT:    ret <2 x b64> <b64 0, b64 4611686019505324032>
+; CHECK-LABEL: @bitcast_constexpr_4f32_2b64_poison(
+; 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
 }
 
 define <2 x b64> @bitcast_constexpr_8b16_2b64_poison() {
-; LE-LABEL: @bitcast_constexpr_8b16_2b64_poison(
-; LE-NEXT:    ret <2 x b64> <b64 8590000128, b64 1970350607106052>
-;
-; BE-LABEL: @bitcast_constexpr_8b16_2b64_poison(
-; BE-NEXT:    ret <2 x b64> <b64 0, b64 1125921382072327>
+; CHECK-LABEL: @bitcast_constexpr_8b16_2b64_poison(
+; 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 52acea045bf832d6b9a86bf597bbbab539f1fe86 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/12] Another poison pre-commit test

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

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index cc1cf52119d83..a73c0d762cc66 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -632,6 +632,17 @@ 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() {
+; LE-LABEL: @bitcast_constexpr_4i32_2b64_all_poison_group(
+; LE-NEXT:    ret <2 x b64> <b64 poison, b64 12884901890>
+;
+; BE-LABEL: @bitcast_constexpr_4i32_2b64_all_poison_group(
+; BE-NEXT:    ret <2 x b64> <b64 poison, b64 8589934595>
+;
+  %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() {
 ; LE-LABEL: @bitcast_constexpr_4b32_2i64_poison(
 ; LE-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>

>From 2c2c48bf4bbbfed558f836bf133aed9b1e0352d0 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/12] [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 +++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index c881ca262b500..42328e1db1ec0 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

>From 2d66229efe2e7a960408e3f5ced4ce9f8e87a928 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/12] More pre-commit tests

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

diff --git a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
index a73c0d762cc66..41ac713b775ba 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -672,3 +672,36 @@ 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() {
+; LE-LABEL: @bitcast_constexpr_2b64_4i32_poison(
+; LE-NEXT:    ret <4 x i32> <i32 poison, i32 poison, i32 1, i32 0>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_4i32_poison(
+; BE-NEXT:    ret <4 x i32> <i32 poison, i32 poison, i32 0, i32 1>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_4b32_2f64_poison(
+; LE-NEXT:    ret <2 x double> <double 0x100000000, double 0x300000002>
+;
+; BE-LABEL: @bitcast_constexpr_4b32_2f64_poison(
+; BE-NEXT:    ret <2 x double> <double 4.940660e-324, double 0x200000003>
+;
+  %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() {
+; LE-LABEL: @bitcast_constexpr_2b64_4f32_poison(
+; LE-NEXT:    ret <4 x float> <float poison, float poison, float 0x36A0000000000000, float 0.000000e+00>
+;
+; BE-LABEL: @bitcast_constexpr_2b64_4f32_poison(
+; BE-NEXT:    ret <4 x float> <float poison, float poison, float 0.000000e+00, float 0x36A0000000000000>
+;
+  %res = bitcast <2 x b64> <b64 poison, b64 1> to <4 x float>
+  ret <4 x float> %res
+}

>From 22f7de4e2f1cbd881db10175abd6c658c6102bc4 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/12] [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         | 37 ++++++++++++++++++-
 .../InstSimplify/bitcast-vector-fold.ll       |  8 ++--
 2 files changed, 40 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 42328e1db1ec0..64396e83810ff 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);
@@ -394,6 +423,12 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
     }
   }
 
+  // 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 41ac713b775ba..fe36a687d7c9f 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -645,10 +645,10 @@ define <2 x b64> @bitcast_constexpr_4i32_2b64_all_poison_group() {
 
 define <2 x i64> @bitcast_constexpr_4b32_2i64_poison() {
 ; LE-LABEL: @bitcast_constexpr_4b32_2i64_poison(
-; LE-NEXT:    ret <2 x i64> <i64 4294967296, i64 12884901890>
+; LE-NEXT:    ret <2 x i64> <i64 poison, i64 12884901890>
 ;
 ; BE-LABEL: @bitcast_constexpr_4b32_2i64_poison(
-; BE-NEXT:    ret <2 x i64> <i64 1, i64 8589934595>
+; BE-NEXT:    ret <2 x i64> <i64 poison, i64 8589934595>
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x i64>
   ret <2 x i64> %res
@@ -686,10 +686,10 @@ define <4 x i32> @bitcast_constexpr_2b64_4i32_poison() {
 
 define <2 x double> @bitcast_constexpr_4b32_2f64_poison() {
 ; LE-LABEL: @bitcast_constexpr_4b32_2f64_poison(
-; LE-NEXT:    ret <2 x double> <double 0x100000000, double 0x300000002>
+; LE-NEXT:    ret <2 x double> <double poison, double 0x300000002>
 ;
 ; BE-LABEL: @bitcast_constexpr_4b32_2f64_poison(
-; BE-NEXT:    ret <2 x double> <double 4.940660e-324, double 0x200000003>
+; BE-NEXT:    ret <2 x double> <double poison, double 0x200000003>
 ;
   %res = bitcast <4 x b32> <b32 poison, b32 1, b32 2, b32 3> to <2 x double>
   ret <2 x double> %res

>From 1f3d8d47fe3c6135fdd53069e5fb2b6cf92cdc94 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/12] 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 fe36a687d7c9f..1d09bab661d39 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -705,3 +705,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 40e492846d72131017f323ba8c57cfd2dd40442f 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/12] [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        | 18 ++++++++++++------
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 64396e83810ff..7d7b9817c5e16 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 1d09bab661d39..8dd37a21417b4 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -707,8 +707,11 @@ 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)
+; LE-LABEL: @bitcast_constexpr_2b64_i128(
+; LE-NEXT:    ret i128 18446744073709551618
+;
+; BE-LABEL: @bitcast_constexpr_2b64_i128(
+; BE-NEXT:    ret i128 36893488147419103233
 ;
   %res = bitcast <2 x b64> <b64 2, b64 1> to i128
   ret i128 %res
@@ -716,15 +719,18 @@ 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
 }
 
 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)
+; LE-LABEL: @bitcast_constexpr_2b32_f64(
+; LE-NEXT:    ret double 0x100000002
+;
+; BE-LABEL: @bitcast_constexpr_2b32_f64(
+; BE-NEXT:    ret double 0x200000001
 ;
   %res = bitcast <2 x b32> <b32 2, b32 1> to double
   ret double %res
@@ -732,7 +738,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 d6dcb923204f5d87b1fc07de88df26b8c4cd1467 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/12] refactor: factor out poison-lane computation

---
 llvm/lib/Analysis/ConstantFolding.cpp | 115 +++++++++-----------------
 1 file changed, 38 insertions(+), 77 deletions(-)

diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 7d7b9817c5e16..af4bfbfde3b2f 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.
@@ -242,60 +275,12 @@ 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(
         IntegerType::get(C->getContext(), ByteWidth), NumDstElt);
-    // Recursively handle this integer conversion, if possible.
     C = FoldBitCast(C, DestIVTy, DL);
-
-    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);
+    return ConstantExpr::getBitCast(C, DestTy);
   }
 
   // Okay, we know the destination is integer, if the input is FP, convert
@@ -317,30 +302,8 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
   // 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);
-      }
-    }
+    if (!computePoisonDstLanes(C, NumSrcElt, NumDstElt, PoisonDstElts))
+      return ConstantExpr::getBitCast(C, DestTy);
 
     unsigned ByteWidth = SrcEltTy->getPrimitiveSizeInBits();
     auto *SrcIVTy = FixedVectorType::get(
@@ -429,10 +392,8 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
   }
 
   // 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);
+  for (unsigned I : PoisonDstElts.set_bits())
+    Result[I] = PoisonValue::get(DstEltTy);
 
   return ConstantVector::get(Result);
 }

>From 4f1208ec70724f12ee7e0de85c089245cc616a68 Mon Sep 17 00:00:00 2001
From: Pedro Lobo <pedro.lobo at tecnico.ulisboa.pt>
Date: Sat, 18 Apr 2026 19:52:11 +0100
Subject: [PATCH 12/12] [ConstantFold] Revert from scalar to vector checks

---
 llvm/lib/IR/ConstantFold.cpp | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/llvm/lib/IR/ConstantFold.cpp b/llvm/lib/IR/ConstantFold.cpp
index a4a2d362b5d5b..ef87b1037beb6 100644
--- a/llvm/lib/IR/ConstantFold.cpp
+++ b/llvm/lib/IR/ConstantFold.cpp
@@ -76,13 +76,13 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
     if (isa<VectorType>(DestTy) && !isa<VectorType>(SrcTy))
       return ConstantExpr::getBitCast(ConstantVector::get(V), DestTy);
 
-    if (DestTy->isByteTy() &&
+    if (DestTy->isByteOrByteVectorTy() &&
         DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
-      return ConstantByte::get(DestTy->getContext(), CI->getValue());
+      return ConstantByte::get(DestTy, CI->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() &&
+    if (DestTy->isFPOrFPVectorTy() && !DestTy->isPPC_FP128Ty() &&
         DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
       return ConstantFP::get(
           DestTy,
@@ -99,13 +99,13 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
     if (isa<VectorType>(DestTy) && !isa<VectorType>(SrcTy))
       return ConstantExpr::getBitCast(ConstantVector::get(V), DestTy);
 
-    if (DestTy->isIntegerTy() &&
+    if (DestTy->isIntOrIntVectorTy() &&
         DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
-      return ConstantInt::get(DestTy->getContext(), CB->getValue());
+      return ConstantInt::get(DestTy, 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() &&
+    if (DestTy->isFPOrFPVectorTy() && !DestTy->isPPC_FP128Ty() &&
         DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
       return ConstantFP::get(
           DestTy,
@@ -143,7 +143,7 @@ static Constant *FoldBitCast(Constant *V, Type *DestTy) {
       return ConstantInt::get(DestTy, FP->getValueAPF().bitcastToAPInt());
 
     // Make sure dest type is compatible with the folded byte constant.
-    if (DestTy->isByteTy() &&
+    if (DestTy->isByteOrByteVectorTy() &&
         DestTy->getScalarSizeInBits() == SrcTy->getScalarSizeInBits())
       return ConstantByte::get(DestTy, FP->getValueAPF().bitcastToAPInt());
 



More information about the llvm-commits mailing list