[llvm] [ConstantFolding] Add folding for [de]interleave2, insert and extract (PR #141301)
Nikolay Panchenko via llvm-commits
llvm-commits at lists.llvm.org
Fri May 23 16:30:54 PDT 2025
https://github.com/npanchen created https://github.com/llvm/llvm-project/pull/141301
The change adds folding for 4 vector intrinsics: `interleave2`, `deinterleave2`, `vector_extract` and `vector_insert`. For the last 2 intrinsics the change does not use `ShuffleVector` fold mechanism as it's much simpler to construct result vector explicitly.
>From 7e7376ed7c8a0bfdd3d56842344e12d9e2551bdf Mon Sep 17 00:00:00 2001
From: Kolya Panchenko <npanchen at modular.com>
Date: Fri, 23 May 2025 15:41:39 -0700
Subject: [PATCH] [ConstantFolding] Add folding for [de]interleave2, insert and
extract
The change adds folding for 4 vector intrinsics: `interleave2`,
`deinterleave2`, `vector_extract` and `vector_insert`.
For the last 2 intrinsics the change does not use `ShuffleVector` fold
mechanism as it's much simpler to construct result vector explicitly.
---
llvm/lib/Analysis/ConstantFolding.cpp | 78 +++++++++++++++++++
.../InstSimplify/ConstProp/vector-calls.ll | 50 ++++++++++++
2 files changed, 128 insertions(+)
create mode 100644 llvm/test/Transforms/InstSimplify/ConstProp/vector-calls.ll
diff --git a/llvm/lib/Analysis/ConstantFolding.cpp b/llvm/lib/Analysis/ConstantFolding.cpp
index 412a0e8979193..d30f2fef69a54 100644
--- a/llvm/lib/Analysis/ConstantFolding.cpp
+++ b/llvm/lib/Analysis/ConstantFolding.cpp
@@ -1619,6 +1619,10 @@ bool llvm::canConstantFoldCallTo(const CallBase *Call, const Function *F) {
case Intrinsic::vector_reduce_smax:
case Intrinsic::vector_reduce_umin:
case Intrinsic::vector_reduce_umax:
+ case Intrinsic::vector_extract:
+ case Intrinsic::vector_insert:
+ case Intrinsic::vector_interleave2:
+ case Intrinsic::vector_deinterleave2:
// Target intrinsics
case Intrinsic::amdgcn_perm:
case Intrinsic::amdgcn_wave_reduce_umin:
@@ -3734,6 +3738,65 @@ static Constant *ConstantFoldFixedVectorCall(
}
return nullptr;
}
+ case Intrinsic::vector_extract: {
+ auto *Vec = dyn_cast<Constant>(Operands[0]);
+ auto *Idx = dyn_cast<ConstantInt>(Operands[1]);
+ if (!Vec || !Idx)
+ return nullptr;
+
+ unsigned NumElements = FVTy->getNumElements();
+ unsigned VecNumElements =
+ cast<FixedVectorType>(Vec->getType())->getNumElements();
+ // Extracting entire vector is nop
+ if (NumElements == VecNumElements)
+ return Vec;
+
+ unsigned StartingIndex = Idx->getZExtValue();
+ assert(StartingIndex + NumElements <= VecNumElements &&
+ "Cannot extract more elements than exist in the vector");
+ for (unsigned I = 0; I != NumElements; ++I)
+ Result[I] = Vec->getAggregateElement(StartingIndex + I);
+ return ConstantVector::get(Result);
+ }
+ case Intrinsic::vector_insert: {
+ auto *Vec = dyn_cast<Constant>(Operands[0]);
+ auto *SubVec = dyn_cast<Constant>(Operands[1]);
+ auto *Idx = dyn_cast<ConstantInt>(Operands[2]);
+ if (!Vec || !SubVec || !Idx)
+ return nullptr;
+
+ unsigned SubVecNumElements =
+ cast<FixedVectorType>(SubVec->getType())->getNumElements();
+ unsigned VecNumElements =
+ cast<FixedVectorType>(Vec->getType())->getNumElements();
+ unsigned IdxN = Idx->getZExtValue();
+ // Replacing entire vector with a subvec is nop
+ if (SubVecNumElements == VecNumElements)
+ return SubVec;
+
+ unsigned I = 0;
+ for (; I < IdxN; ++I)
+ Result[I] = Vec->getAggregateElement(I);
+ for (; I < IdxN + SubVecNumElements; ++I)
+ Result[I] = SubVec->getAggregateElement(I - IdxN);
+ for (; I < VecNumElements; ++I)
+ Result[I] = Vec->getAggregateElement(I);
+ return ConstantVector::get(Result);
+ }
+ case Intrinsic::vector_interleave2: {
+ auto *Vec0 = dyn_cast<Constant>(Operands[0]);
+ auto *Vec1 = dyn_cast<Constant>(Operands[1]);
+ if (!Vec0 || !Vec1)
+ return nullptr;
+
+ unsigned NumElements =
+ cast<FixedVectorType>(Vec0->getType())->getNumElements();
+ for (unsigned I = 0; I < NumElements; ++I) {
+ Result[2 * I] = Vec0->getAggregateElement(I);
+ Result[2 * I + 1] = Vec1->getAggregateElement(I);
+ }
+ return ConstantVector::get(Result);
+ }
default:
break;
}
@@ -3872,6 +3935,21 @@ ConstantFoldStructCall(StringRef Name, Intrinsic::ID IntrinsicID,
return nullptr;
return ConstantStruct::get(StTy, SinResult, CosResult);
}
+ case Intrinsic::vector_deinterleave2: {
+ auto *Vec = dyn_cast<Constant>(Operands[0]);
+ if (!Vec)
+ return nullptr;
+
+ unsigned NumElements =
+ cast<FixedVectorType>(Vec->getType())->getNumElements() / 2;
+ SmallVector<Constant *, 4> Res0(NumElements), Res1(NumElements);
+ for (unsigned I = 0; I < NumElements; ++I) {
+ Res0[I] = Vec->getAggregateElement(2 * I);
+ Res1[I] = Vec->getAggregateElement(2 * I + 1);
+ }
+ return ConstantStruct::get(StTy, ConstantVector::get(Res0),
+ ConstantVector::get(Res1));
+ }
default:
// TODO: Constant folding of vector intrinsics that fall through here does
// not work (e.g. overflow intrinsics)
diff --git a/llvm/test/Transforms/InstSimplify/ConstProp/vector-calls.ll b/llvm/test/Transforms/InstSimplify/ConstProp/vector-calls.ll
new file mode 100644
index 0000000000000..f0bf610fa52aa
--- /dev/null
+++ b/llvm/test/Transforms/InstSimplify/ConstProp/vector-calls.ll
@@ -0,0 +1,50 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 5
+; RUN: opt < %s -passes=instsimplify,verify -S | FileCheck %s
+
+define <3 x i32> @fold_vector_extract() {
+; CHECK-LABEL: define <3 x i32> @fold_vector_extract() {
+; CHECK-NEXT: ret <3 x i32> <i32 3, i32 4, i32 5>
+;
+ %1 = call <3 x i32> @llvm.vector.extract.v3i32.v8i32(<8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>, i64 3)
+ ret <3 x i32> %1
+}
+
+define <8 x i32> @fold_vector_extract_nop() {
+; CHECK-LABEL: define <8 x i32> @fold_vector_extract_nop() {
+; CHECK-NEXT: ret <8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>
+;
+ %1 = call <8 x i32> @llvm.vector.extract.v3i32.v8i32(<8 x i32> <i32 0, i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7>, i64 0)
+ ret <8 x i32> %1
+}
+
+define <8 x i32> @fold_vector_insert() {
+; CHECK-LABEL: define <8 x i32> @fold_vector_insert() {
+; CHECK-NEXT: ret <8 x i32> <i32 9, i32 10, i32 11, i32 12, i32 5, i32 6, i32 7, i32 8>
+;
+ %1 = call <8 x i32> @llvm.vector.insert.v8i32(<8 x i32> <i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8>, <4 x i32> <i32 9, i32 10, i32 11, i32 12>, i64 0)
+ ret <8 x i32> %1
+}
+
+define <8 x i32> @fold_vector_insert_nop() {
+; CHECK-LABEL: define <8 x i32> @fold_vector_insert_nop() {
+; CHECK-NEXT: ret <8 x i32> <i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18>
+;
+ %1 = call <8 x i32> @llvm.vector.insert.v8i32(<8 x i32> <i32 1, i32 2, i32 3, i32 4, i32 5, i32 6, i32 7, i32 8>, <8 x i32> <i32 11, i32 12, i32 13, i32 14, i32 15, i32 16, i32 17, i32 18>, i64 0)
+ ret <8 x i32> %1
+}
+
+define <8 x i32> @fold_vector_interleave2() {
+; CHECK-LABEL: define <8 x i32> @fold_vector_interleave2() {
+; CHECK-NEXT: ret <8 x i32> <i32 1, i32 5, i32 2, i32 6, i32 3, i32 7, i32 4, i32 8>
+;
+ %1 = call<8 x i32> @llvm.vector.interleave2.v8i32(<4 x i32> <i32 1, i32 2, i32 3, i32 4>, <4 x i32> <i32 5, i32 6, i32 7, i32 8>)
+ ret <8 x i32> %1
+}
+
+define {<4 x i32>, <4 x i32>} @fold_vector_deinterleav2() {
+; CHECK-LABEL: define { <4 x i32>, <4 x i32> } @fold_vector_deinterleav2() {
+; CHECK-NEXT: ret { <4 x i32>, <4 x i32> } { <4 x i32> <i32 1, i32 2, i32 3, i32 4>, <4 x i32> <i32 5, i32 6, i32 7, i32 8> }
+;
+ %1 = call {<4 x i32>, <4 x i32>} @llvm.vector.deinterleave2.v4i32.v8i32(<8 x i32> <i32 1, i32 5, i32 2, i32 6, i32 3, i32 7, i32 4, i32 8>)
+ ret {<4 x i32>, <4 x i32>} %1
+}
More information about the llvm-commits
mailing list