[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