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

Pedro Lobo via llvm-commits llvm-commits at lists.llvm.org
Sat Apr 4 09:05:33 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 1/4] 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 2/4] [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 3/4] 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 4/4] [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



More information about the llvm-commits mailing list