[llvm] [VectorCombine] Fold bitcast(bitreverse(bitcast(x))) to fshl+bitreverse (PR #214362)
Björn Pettersson via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 10 05:01:13 PDT 2026
https://github.com/bjope updated https://github.com/llvm/llvm-project/pull/214362
>From 95fb1b05ff4207c1833ae970a2a1ee903e454a31 Mon Sep 17 00:00:00 2001
From: Bjorn Pettersson <bjorn.a.pettersson at ericsson.com>
Date: Thu, 6 Aug 2026 00:16:16 +0200
Subject: [PATCH 1/3] [VectorCombine] Fold bitcast(bitreverse(bitcast(x))) to
fshl+bitreverse
Extend foldBitOrderReverseAndSwap to handle <2 x iN> element
bitreverse patterns. Previously the fold only handled <N x i8> vectors
using bswap to reorder octets. For 2-element vectors, the element
swap is a rotate by half the bitwidth, which can be expressed as
fshl(x, x, bitwidth/2).
The transform rewrites:
bitcast(bitreverse(bitcast(x))) -> bitreverse(fshl(x, x, N/2))
when the cost model indicates the scalar form is cheaper.
For <2 x i8> we still prefer to use bswap, and we do not even
consider to check if fshl is cheaper than bswap.
---
.../Transforms/Vectorize/VectorCombine.cpp | 39 ++++++++++---
.../AArch64/bitreverse-v2iN-to-scalar.ll | 55 +++++++++++++++++++
2 files changed, 85 insertions(+), 9 deletions(-)
create mode 100644 llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v2iN-to-scalar.ll
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 726f564b1aad9..131ee3ab592b5 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6124,18 +6124,31 @@ bool VectorCombine::foldBitcastOfVPLoad(Instruction &I) {
}
/// Fold the following cases into a single byte-level bit-reverse operation
/// and accepts bswap and bitreverse intrinsics:
-/// bswap(bitreverse(x)) <--> bitcast(bitreverse(bitcast(x)))
+/// bswap(bitreverse(x)) --> bitcast(bitreverse(bitcast(x)))
/// bitreverse(bswap(x)) <--> bitcast(bitreverse(bitcast(x)))
/// The direction of the fold is cost-model driven.
+/// Also supports:
+/// bitcast(bitreverse(bitcast(x))) --> bitreverse(fshl(x))
bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
Value *X;
if (match(&I, m_BitCast(m_BitReverse(m_BitCast(m_Value(X)))))) {
Type *Ty = X->getType();
Type *VecTy = I.getOperand(0)->getType();
- if (Ty->isIntegerTy() && Ty == I.getType() && isa<FixedVectorType>(VecTy) &&
+ // Detect the case when bitreversing every octet in X individually. Then we
+ // can use bswap to reorder the octets before doing a single bitreverse.
+ bool CanUseBswap =
+ Ty->isIntegerTy() && Ty == I.getType() && isa<FixedVectorType>(VecTy) &&
cast<FixedVectorType>(VecTy)->getElementType()->isIntegerTy(8) &&
- Ty->getIntegerBitWidth() % 16 == 0) {
+ Ty->getIntegerBitWidth() % 16 == 0;
+ // Detect the case when bitreversing upper and lower half of X
+ // individually. Then we can use fshl as a rotate operation, to swap the
+ // halves before doing a single bitreverse.
+ bool CanUseFshl =
+ Ty->isIntegerTy() && Ty == I.getType() && isa<FixedVectorType>(VecTy) &&
+ cast<FixedVectorType>(VecTy)->getElementType()->isIntegerTy() &&
+ cast<FixedVectorType>(VecTy)->getNumElements() == 2;
+ if (CanUseBswap || CanUseFshl) {
auto *InnerCall = dyn_cast<Instruction>(I.getOperand(0));
if (!InnerCall)
return false;
@@ -6146,9 +6159,12 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
TTI.getInstructionCost(InnerCall, CostKind) +
TTI.getInstructionCost(&I, CostKind);
IntrinsicCostAttributes ICABSwap(Intrinsic::bswap, Ty, {Ty});
+ IntrinsicCostAttributes ICABFshl(Intrinsic::fshl, Ty, {Ty, Ty, Ty});
IntrinsicCostAttributes ICABRev(Intrinsic::bitreverse, Ty, {Ty});
- InstructionCost NewCost = TTI.getIntrinsicInstrCost(ICABSwap, CostKind) +
- TTI.getIntrinsicInstrCost(ICABRev, CostKind);
+ InstructionCost NewCost =
+ TTI.getIntrinsicInstrCost(CanUseBswap ? ICABSwap : ICABFshl,
+ CostKind) +
+ TTI.getIntrinsicInstrCost(ICABRev, CostKind);
if (!InnerCall->hasOneUse())
NewCost += TTI.getInstructionCost(InnerCall, CostKind) +
TTI.getInstructionCost(InnerBitCast, CostKind);
@@ -6159,10 +6175,15 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
<< " vs NewCost: " << NewCost << "\n");
if (NewCost.isValid() && NewCost < OldCost) {
Builder.SetInsertPoint(&I);
- Value *BSwap = Builder.CreateUnaryIntrinsic(Intrinsic::bswap, X);
- Worklist.pushValue(BSwap);
- Value *BRev =
- Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, BSwap);
+ Value *Swap =
+ CanUseBswap
+ ? Builder.CreateUnaryIntrinsic(Intrinsic::bswap, X)
+ : Builder.CreateIntrinsic(
+ Ty, Intrinsic::fshl,
+ {X, X,
+ ConstantInt::get(Ty, Ty->getIntegerBitWidth() / 2)});
+ Worklist.pushValue(Swap);
+ Value *BRev = Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, Swap);
replaceValue(I, *BRev);
return true;
}
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v2iN-to-scalar.ll b/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v2iN-to-scalar.ll
new file mode 100644
index 0000000000000..d9ce89e05b4d8
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v2iN-to-scalar.ll
@@ -0,0 +1,55 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=vector-combine -mtriple=aarch64 < %s | FileCheck %s
+
+define i6 @fold_v2i3_roundtrip_i6(i6 %x) {
+; CHECK-LABEL: define i6 @fold_v2i3_roundtrip_i6(
+; CHECK-SAME: i6 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call i6 @llvm.fshl.i6(i6 [[X]], i6 [[X]], i6 3)
+; CHECK-NEXT: [[TMP2:%.*]] = call i6 @llvm.bitreverse.i6(i6 [[TMP1]])
+; CHECK-NEXT: ret i6 [[TMP2]]
+;
+ %1 = bitcast i6 %x to <2 x i3>
+ %2 = call <2 x i3> @llvm.bitreverse.v2i3(<2 x i3> %1)
+ %3 = bitcast <2 x i3> %2 to i6
+ ret i6 %3
+}
+
+define i16 @fold_v2i8_roundtrip_i16(i16 %x) {
+; CHECK-LABEL: define i16 @fold_v2i8_roundtrip_i16(
+; CHECK-SAME: i16 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call i16 @llvm.bswap.i16(i16 [[X]])
+; CHECK-NEXT: [[TMP2:%.*]] = call i16 @llvm.bitreverse.i16(i16 [[TMP1]])
+; CHECK-NEXT: ret i16 [[TMP2]]
+;
+ %1 = bitcast i16 %x to <2 x i8>
+ %2 = call <2 x i8> @llvm.bitreverse.v2i8(<2 x i8> %1)
+ %3 = bitcast <2 x i8> %2 to i16
+ ret i16 %3
+}
+
+define i64 @fold_v2i32_roundtrip_i64(i64 %x) {
+; CHECK-LABEL: define i64 @fold_v2i32_roundtrip_i64(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call i64 @llvm.fshl.i64(i64 [[X]], i64 [[X]], i64 32)
+; CHECK-NEXT: [[TMP2:%.*]] = call i64 @llvm.bitreverse.i64(i64 [[TMP1]])
+; CHECK-NEXT: ret i64 [[TMP2]]
+;
+ %1 = bitcast i64 %x to <2 x i32>
+ %2 = call <2 x i32> @llvm.bitreverse.v2i32(<2 x i32> %1)
+ %3 = bitcast <2 x i32> %2 to i64
+ ret i64 %3
+}
+
+define i128 @fold_v2i64_roundtrip_i128(i128 %x) {
+; CHECK-LABEL: define i128 @fold_v2i64_roundtrip_i128(
+; CHECK-SAME: i128 [[X:%.*]]) {
+; CHECK-NEXT: [[TMP1:%.*]] = call i128 @llvm.fshl.i128(i128 [[X]], i128 [[X]], i128 64)
+; CHECK-NEXT: [[TMP2:%.*]] = call i128 @llvm.bitreverse.i128(i128 [[TMP1]])
+; CHECK-NEXT: ret i128 [[TMP2]]
+;
+ %1 = bitcast i128 %x to <2 x i64>
+ %2 = call <2 x i64> @llvm.bitreverse.v2i64(<2 x i64> %1)
+ %3 = bitcast <2 x i64> %2 to i128
+ ret i128 %3
+}
+
>From bc3a370f82aba343f6aaa373996005bc4300b5fb Mon Sep 17 00:00:00 2001
From: Bjorn Pettersson <bjorn.a.pettersson at ericsson.com>
Date: Mon, 10 Aug 2026 13:55:12 +0200
Subject: [PATCH 2/3] Fixup: Hint that fshl is a rotate
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 131ee3ab592b5..bafc47a225c6e 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6155,11 +6155,12 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
auto *InnerBitCast = dyn_cast<BitCastInst>(InnerCall->getOperand(0));
if (!InnerBitCast)
return false;
+ Constant *HalfBW = ConstantInt::get(Ty, Ty->getIntegerBitWidth() / 2);
InstructionCost OldCost = TTI.getInstructionCost(InnerBitCast, CostKind) +
TTI.getInstructionCost(InnerCall, CostKind) +
TTI.getInstructionCost(&I, CostKind);
IntrinsicCostAttributes ICABSwap(Intrinsic::bswap, Ty, {Ty});
- IntrinsicCostAttributes ICABFshl(Intrinsic::fshl, Ty, {Ty, Ty, Ty});
+ IntrinsicCostAttributes ICABFshl(Intrinsic::fshl, Ty, {X,X,HalfBW}, {Ty, Ty, Ty});
IntrinsicCostAttributes ICABRev(Intrinsic::bitreverse, Ty, {Ty});
InstructionCost NewCost =
TTI.getIntrinsicInstrCost(CanUseBswap ? ICABSwap : ICABFshl,
@@ -6180,8 +6181,7 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
? Builder.CreateUnaryIntrinsic(Intrinsic::bswap, X)
: Builder.CreateIntrinsic(
Ty, Intrinsic::fshl,
- {X, X,
- ConstantInt::get(Ty, Ty->getIntegerBitWidth() / 2)});
+ {X, X, HalfBW});
Worklist.pushValue(Swap);
Value *BRev = Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, Swap);
replaceValue(I, *BRev);
>From f88e590f6293a592d1c75f3928de1962fd8b1ea8 Mon Sep 17 00:00:00 2001
From: Bjorn Pettersson <bjorn.a.pettersson at ericsson.com>
Date: Mon, 10 Aug 2026 14:00:57 +0200
Subject: [PATCH 3/3] clang-format update
---
llvm/lib/Transforms/Vectorize/VectorCombine.cpp | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index bafc47a225c6e..a435d936bdb2c 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6160,7 +6160,8 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
TTI.getInstructionCost(InnerCall, CostKind) +
TTI.getInstructionCost(&I, CostKind);
IntrinsicCostAttributes ICABSwap(Intrinsic::bswap, Ty, {Ty});
- IntrinsicCostAttributes ICABFshl(Intrinsic::fshl, Ty, {X,X,HalfBW}, {Ty, Ty, Ty});
+ IntrinsicCostAttributes ICABFshl(Intrinsic::fshl, Ty, {X, X, HalfBW},
+ {Ty, Ty, Ty});
IntrinsicCostAttributes ICABRev(Intrinsic::bitreverse, Ty, {Ty});
InstructionCost NewCost =
TTI.getIntrinsicInstrCost(CanUseBswap ? ICABSwap : ICABFshl,
@@ -6179,9 +6180,7 @@ bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
Value *Swap =
CanUseBswap
? Builder.CreateUnaryIntrinsic(Intrinsic::bswap, X)
- : Builder.CreateIntrinsic(
- Ty, Intrinsic::fshl,
- {X, X, HalfBW});
+ : Builder.CreateIntrinsic(Ty, Intrinsic::fshl, {X, X, HalfBW});
Worklist.pushValue(Swap);
Value *BRev = Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, Swap);
replaceValue(I, *BRev);
More information about the llvm-commits
mailing list