[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