[llvm] [VectorCombine] Fold bitcast(bitreverse.v8i8(bitcast(IntTy))) into bswap+bitreverse (PR #209037)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 12 11:09:33 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Deepak Shirke (deepakshirkem)

<details>
<summary>Changes</summary>

Add reverse direction to `foldBitOrderReverseAndSwap`: bitcast(bitreverse(<N x i8>)(bitcast(IntTy X))) --> bitreverse(bswap(X))

This avoids GPR<=>vector register crossings when integer ops are cheaper. On AArch64 for example:

Before:
  fmov    d0, x0        ; GPR → vector crossing
  rbit    v0.8b, v0.8b
  fmov    x0, d0        ; vector → GPR crossing

After:
  rev     x8, x0        ; stays in integer registers
  rbit    x0, x8

The fold is cost-model driven and only fires when the integer version is cheaper, so targets like X86+GFNI that prefer the vector form (single `vgf2p8affineqb` instruction) are unaffected.

A new call site for `BitCast` instructions is added alongside the existing `Call` instruction call site in the dispatch loop.

Fixes https://github.com/llvm/llvm-project/issues/201760

---
Full diff: https://github.com/llvm/llvm-project/pull/209037.diff


2 Files Affected:

- (modified) llvm/lib/Transforms/Vectorize/VectorCombine.cpp (+42) 
- (added) llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v8i8-to-gpr.ll (+60) 


``````````diff
diff --git a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
index 8648125222e2a..d4387527dd2bc 100644
--- a/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
+++ b/llvm/lib/Transforms/Vectorize/VectorCombine.cpp
@@ -6118,6 +6118,45 @@ bool VectorCombine::foldBitcastOfVPLoad(Instruction &I) {
 ///   bitreverse(bswap(x)) --> bitcast(bitreverse(bitcast(x)))
 bool VectorCombine::foldBitOrderReverseAndSwap(Instruction &I) {
   Value *X;
+
+  if (match(&I, m_BitCast(m_Intrinsic<Intrinsic::bitreverse>(
+                    m_BitCast(m_Value(X)))))) {
+    Type *Ty = X->getType();
+    Type *VecTy = cast<BitCastInst>(&I)->getOperand(0)->getType();
+    if (Ty->isIntegerTy() && Ty == I.getType() && isa<FixedVectorType>(VecTy) &&
+        cast<FixedVectorType>(VecTy)->getElementType()->isIntegerTy(8) &&
+        Ty->getIntegerBitWidth() % 16 == 0) {
+      auto *InnerCall = cast<Instruction>(cast<BitCastInst>(&I)->getOperand(0));
+      // OldCost = bitcast to vec + bitreverse.v8i8 + bitcast to int
+      InstructionCost OldCost =
+          TTI.getCastInstrCost(Instruction::BitCast, VecTy, Ty,
+                               TTI::CastContextHint::None, CostKind) +
+          TTI.getInstructionCost(InnerCall, CostKind) +
+          TTI.getCastInstrCost(Instruction::BitCast, Ty, VecTy,
+                               TTI::CastContextHint::None, CostKind);
+      // NewCost = bswap + bitreverse on integer type.
+      // If bitreverse.v8i8 has other uses we cannot remove it,
+      // so add its cost to NewCost.
+      IntrinsicCostAttributes ICABSwap(Intrinsic::bswap, Ty, {Ty});
+      IntrinsicCostAttributes ICABRev(Intrinsic::bitreverse, Ty, {Ty});
+      InstructionCost NewCost = TTI.getIntrinsicInstrCost(ICABSwap, CostKind) +
+                                TTI.getIntrinsicInstrCost(ICABRev, CostKind);
+      if (!InnerCall->hasOneUse())
+        NewCost += TTI.getInstructionCost(InnerCall, CostKind);
+      LLVM_DEBUG(dbgs() << "Found bitreverse vector roundtrip: " << I
+                        << "\n  OldCost: " << OldCost
+                        << " vs NewCost: " << NewCost << "\n");
+      if (NewCost.isValid() && NewCost < OldCost) {
+        Builder.SetInsertPoint(&I);
+        Value *BSwap = Builder.CreateUnaryIntrinsic(Intrinsic::bswap, X);
+        Value *BRev =
+            Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, BSwap);
+        replaceValue(I, *BRev);
+        return true;
+      }
+    }
+  }
+
   if (!match(&I, m_BitReverse(m_BSwap(m_Value(X)))) &&
       !match(&I, m_BSwap(m_BitReverse(m_Value(X)))))
     return false;
@@ -6464,6 +6503,9 @@ bool VectorCombine::run() {
     if (Opcode == Instruction::Call)
       if (foldBitOrderReverseAndSwap(I))
         return true;
+    if (Opcode == Instruction::BitCast)
+      if (foldBitOrderReverseAndSwap(I))
+        return true;
 
     // Otherwise, try folds that improve codegen but may interfere with
     // early IR canonicalizations.
diff --git a/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v8i8-to-gpr.ll b/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v8i8-to-gpr.ll
new file mode 100644
index 0000000000000..4fd34987b2d39
--- /dev/null
+++ b/llvm/test/Transforms/VectorCombine/AArch64/bitreverse-v8i8-to-gpr.ll
@@ -0,0 +1,60 @@
+; 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 i64 @fold_v8i8_roundtrip_i64(i64 %x) {
+; CHECK-LABEL: define i64 @fold_v8i8_roundtrip_i64(
+; CHECK-SAME: i64 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call i64 @llvm.bswap.i64(i64 [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = call i64 @llvm.bitreverse.i64(i64 [[TMP1]])
+; CHECK-NEXT:    ret i64 [[TMP2]]
+;
+  %1 = bitcast i64 %x to <8 x i8>
+  %2 = call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> %1)
+  %3 = bitcast <8 x i8> %2 to i64
+  ret i64 %3
+}
+
+define i32 @fold_v4i8_roundtrip_i32(i32 %x) {
+; CHECK-LABEL: define i32 @fold_v4i8_roundtrip_i32(
+; CHECK-SAME: i32 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = call i32 @llvm.bswap.i32(i32 [[X]])
+; CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.bitreverse.i32(i32 [[TMP1]])
+; CHECK-NEXT:    ret i32 [[TMP2]]
+;
+  %1 = bitcast i32 %x to <4 x i8>
+  %2 = call <4 x i8> @llvm.bitreverse.v4i8(<4 x i8> %1)
+  %3 = bitcast <4 x i8> %2 to i32
+  ret i32 %3
+}
+
+; Negative test: i24 has odd byte count, bswap requires even bytes
+define i24 @no_fold_v3i8_roundtrip_i24(i24 %x) {
+; CHECK-LABEL: define i24 @no_fold_v3i8_roundtrip_i24(
+; CHECK-SAME: i24 [[X:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast i24 [[X]] to <3 x i8>
+; CHECK-NEXT:    [[TMP2:%.*]] = call <3 x i8> @llvm.bitreverse.v3i8(<3 x i8> [[TMP1]])
+; CHECK-NEXT:    [[TMP3:%.*]] = bitcast <3 x i8> [[TMP2]] to i24
+; CHECK-NEXT:    ret i24 [[TMP3]]
+;
+  %1 = bitcast i24 %x to <3 x i8>
+  %2 = call <3 x i8> @llvm.bitreverse.v3i8(<3 x i8> %1)
+  %3 = bitcast <3 x i8> %2 to i24
+  ret i24 %3
+}
+
+; Negative test: multiple uses of bitreverse result should NOT fold
+define i64 @no_fold_multiuse(i64 %x, ptr %p) {
+; CHECK-LABEL: define i64 @no_fold_multiuse(
+; CHECK-SAME: i64 [[X:%.*]], ptr [[P:%.*]]) {
+; CHECK-NEXT:    [[TMP1:%.*]] = bitcast i64 [[X]] to <8 x i8>
+; CHECK-NEXT:    [[TMP2:%.*]] = call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> [[TMP1]])
+; CHECK-NEXT:    store <8 x i8> [[TMP2]], ptr [[P]], align 8
+; CHECK-NEXT:    [[TMP3:%.*]] = bitcast <8 x i8> [[TMP2]] to i64
+; CHECK-NEXT:    ret i64 [[TMP3]]
+;
+  %1 = bitcast i64 %x to <8 x i8>
+  %2 = call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> %1)
+  store <8 x i8> %2, ptr %p
+  %3 = bitcast <8 x i8> %2 to i64
+  ret i64 %3
+}

``````````

</details>


https://github.com/llvm/llvm-project/pull/209037


More information about the llvm-commits mailing list