[llvm] [ConstantFolding] Support bitcasting vectors to smaller element sizes with non-integer ratios. (PR #179640)
Kacper Doga via llvm-commits
llvm-commits at lists.llvm.org
Wed Feb 11 03:03:13 PST 2026
https://github.com/varev-dev updated https://github.com/llvm/llvm-project/pull/179640
>From 3d54070bdc4d35e1a727a4287fce4f42c909ffa1 Mon Sep 17 00:00:00 2001
From: "Doga, Kacper" <kacper.doga at intel.com>
Date: Wed, 4 Feb 2026 09:37:02 +0000
Subject: [PATCH] [ConstantFolding] Support bitcasting vectors to smaller
element sizes with non-integer ratios.
Fixes #179626
---
llvm/lib/Analysis/ConstantFolding.cpp | 62 ++++++++++++++-----
.../InstSimplify/bitcast-vector-fold.ll | 24 +++++++
2 files changed, 69 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index c0754d3a41264..ea6e8297bd732 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -217,13 +217,12 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
// conversion here, which depends on whether the input or output has
// more elements.
bool isLittleEndian = DL.isLittleEndian();
-
+ unsigned SrcBitSize = SrcEltTy->getPrimitiveSizeInBits();
SmallVector<Constant*, 32> Result;
if (NumDstElt < NumSrcElt) {
// Handle: bitcast (<4 x i32> <i32 0, i32 1, i32 2, i32 3> to <2 x i64>)
Constant *Zero = Constant::getNullValue(DstEltTy);
- unsigned Ratio = NumSrcElt/NumDstElt;
- unsigned SrcBitSize = SrcEltTy->getPrimitiveSizeInBits();
+ unsigned Ratio = NumSrcElt / NumDstElt;
unsigned SrcElt = 0;
for (unsigned i = 0; i != NumDstElt; ++i) {
// Build each element of the result.
@@ -262,36 +261,65 @@ Constant *FoldBitCast(Constant *C, Type *DestTy, const DataLayout &DL) {
}
// Handle: bitcast (<2 x i64> <i64 0, i64 1> to <4 x i32>)
+ // bitcast (<3 x i64> <i64 0, i64 1, i64 2> to <8 x i24>)
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);
-
+ APInt Rest(SrcBitSize, 0);
+ unsigned RestSize = 0;
+ for (unsigned i = 0; i != NumSrcElt || Result.size() != NumDstElt;) {
+ unsigned UnusedBits = SrcBitSize - RestSize;
+ if (RestSize >= DstBitSize) {
+ APInt Elt = isLittleEndian ? Rest.lshr(UnusedBits)
+ : Rest << (RestSize - DstBitSize);
+ Result.push_back(ConstantInt::get(DstEltTy, Elt.trunc(DstBitSize)));
+ RestSize -= DstBitSize;
+ Rest.clearBits(UnusedBits - 1, UnusedBits - 1 + DstBitSize);
+ continue;
+ }
+ auto *Element = C->getAggregateElement(i++);
if (!Element) // Reject constantexpr elements.
return ConstantExpr::getBitCast(C, DestTy);
+ APInt NextVecElem;
if (isa<UndefValue>(Element)) {
// Correctly Propagate undef values.
- Result.append(Ratio, UndefValue::get(DstEltTy));
- continue;
+ if (SrcBitSize % DstBitSize == 0) {
+ Result.append(Ratio, UndefValue::get(DstEltTy));
+ continue;
+ }
+ // Replace Undef with zeros to prevent partial corruption.
+ NextVecElem = APInt::getZero(SrcBitSize);
+ } else {
+ auto *Src = dyn_cast<ConstantInt>(Element);
+ if (!Src)
+ return ConstantExpr::getBitCast(C, DestTy);
+ NextVecElem = Src->getValue();
}
-
- auto *Src = dyn_cast<ConstantInt>(Element);
- if (!Src)
- return ConstantExpr::getBitCast(C, DestTy);
-
- unsigned ShiftAmt = isLittleEndian ? 0 : DstBitSize*(Ratio-1);
+ APInt Value = Rest;
+ if (SrcBitSize % DstBitSize)
+ Rest = NextVecElem;
+ if (RestSize != 0) {
+ // Shift the Rest into the right place, shift NextVecElem to fit Rest.
+ if (isLittleEndian) {
+ Value.lshrInPlace(UnusedBits == SrcBitSize ? 0 : UnusedBits);
+ NextVecElem <<= RestSize;
+ } else {
+ Value <<= UnusedBits;
+ NextVecElem.lshrInPlace(RestSize);
+ }
+ }
+ Value |= NextVecElem;
+ unsigned ShiftAmt = isLittleEndian ? 0 : SrcBitSize - DstBitSize;
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);
+ APInt Elt = Value.lshr(ShiftAmt);
ShiftAmt += isLittleEndian ? DstBitSize : -DstBitSize;
// Truncate and remember this piece.
Result.push_back(ConstantInt::get(DstEltTy, Elt.trunc(DstBitSize)));
}
+ RestSize = SrcBitSize + RestSize - Ratio * DstBitSize;
}
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 d2656e291547c..3ad83ba18d64c 100644
--- a/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
+++ b/llvm/test/Transforms/InstSimplify/bitcast-vector-fold.ll
@@ -83,6 +83,22 @@ define <1 x i1> @test10() {
ret <1 x i1> %ret
}
+define <4 x i24> @test11() {
+; CHECK-LABEL: @test11(
+; CHECK-NEXT: ret <4 x i24> <i24 1, i24 256, i24 65536, i24 0>
+;
+ %c = bitcast <3 x i32> <i32 1, i32 1, i32 1> to <4 x i24>
+ ret <4 x i24> %c
+}
+
+define <8 x i24> @test12() {
+; CHECK-LABEL: @test12(
+; CHECK-NEXT: ret <8 x i24> splat (i24 1)
+;
+ %c = bitcast <3 x i64> <i64 281474993487873, i64 72057598332895488, i64 1099511693312> to <8 x i24>
+ ret <8 x i24> %c
+}
+
; from MultiSource/Benchmarks/Bullet
define <2 x float> @foo() {
; CHECK-LABEL: @foo(
@@ -277,6 +293,14 @@ define <16 x i8> @bitcast_constexpr_16i8_8i16_u256uuu256uu() {
ret <16 x i8> %cast
}
+define <4 x i24> @bitcast_constexpr_4i24_3i32_u1u() {
+; CHECK-LABEL: @bitcast_constexpr_4i24_3i32_u1u(
+; CHECK-NEXT: ret <4 x i24> <i24 0, i24 256, i24 0, i24 0>
+;
+ %cast = bitcast <3 x i32><i32 undef, i32 1, i32 undef> to <4 x i24>
+ ret <4 x i24> %cast
+}
+
define <1 x i32> @bitcast_constexpr_scalar_fp_to_vector_int() {
; CHECK-LABEL: @bitcast_constexpr_scalar_fp_to_vector_int(
; CHECK-NEXT: ret <1 x i32> splat (i32 1065353216)
More information about the llvm-commits
mailing list