[llvm] [InstCombine] fold nested bitreverse and bswap (PR #202236)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 7 15:19:54 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Kenny Lau (lauk20)

<details>
<summary>Changes</summary>

Addresses #<!-- -->201756

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


2 Files Affected:

- (modified) llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp (+44) 
- (added) llvm/test/Transforms/InstCombine/bitreverse-bswap-fold.ll (+63) 


``````````diff
diff --git a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
index 7bcaa930511ee..4952f1a80c21c 100644
--- a/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
+++ b/llvm/lib/Transforms/InstCombine/InstCombineCalls.cpp
@@ -1643,6 +1643,41 @@ static Instruction *foldBitOrderCrossLogicOp(Value *V,
   return nullptr;
 }
 
+/// 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)))
+///   bitreverse(bswap(x)) --> bitcast(bitreverse(bitcast(x)))
+template <Intrinsic::ID IntrID>
+static Value *foldBitOrderReverseAndSwap(IntrinsicInst *II,
+                                             InstCombiner::BuilderTy &Builder) {
+  static_assert(IntrID == Intrinsic::bswap || IntrID == Intrinsic::bitreverse,
+                "This helper only supports BSWAP and BITREVERSE intrinsics");
+
+  Value *Arg = II->getArgOperand(0);
+  Type *Ty = II->getType();
+
+  if (Ty->getScalarSizeInBits() <= 8)
+    return nullptr;
+
+  constexpr Intrinsic::ID ComplementID = (IntrID == Intrinsic::bitreverse) 
+                                 ? Intrinsic::bswap 
+                                 : Intrinsic::bitreverse;
+
+  Value *X;
+  if (match(Arg, m_OneUse(m_Intrinsic<ComplementID>(m_Value(X))))) {
+    unsigned TotalBits = Ty->getPrimitiveSizeInBits();
+    Type *I8Ty = Builder.getInt8Ty();
+    Type *NewVecTy = VectorType::get(I8Ty, ElementCount::getFixed(TotalBits / 8));
+
+    // Cast to <N x i8>, perform a bitreverse, and cast back
+    Value *CastIn = Builder.CreateBitCast(X, NewVecTy);
+    Value *NewCall = Builder.CreateUnaryIntrinsic(Intrinsic::bitreverse, CastIn);
+    return Builder.CreateBitCast(NewCall, Ty);
+  }
+
+  return nullptr;
+}
+
 /// Helper to match idempotent binary intrinsics, namely, intrinsics where
 /// `f(f(x, y), y) == f(x, y)` holds.
 static bool isIdempotentBinaryIntrinsic(Intrinsic::ID IID) {
@@ -2420,6 +2455,10 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     if (Instruction *crossLogicOpFold =
         foldBitOrderCrossLogicOp<Intrinsic::bitreverse>(IIOperand, Builder))
       return crossLogicOpFold;
+    
+    if (Value *swapReverseSwapFold = 
+        foldBitOrderReverseAndSwap<Intrinsic::bitreverse>(II, Builder))
+      return replaceInstUsesWith(*II, swapReverseSwapFold);
 
     break;
   }
@@ -2476,6 +2515,11 @@ Instruction *InstCombinerImpl::visitCallInst(CallInst &CI) {
     if (Instruction *BitOp = matchBSwapOrBitReverse(*II, /*MatchBSwaps*/ false,
                                                     /*MatchBitReversals*/ true))
       return BitOp;
+
+    if (Value *swapReverseSwapFold = 
+        foldBitOrderReverseAndSwap<Intrinsic::bswap>(II, Builder))
+      return replaceInstUsesWith(*II, swapReverseSwapFold);
+
     break;
   }
   case Intrinsic::masked_load:
diff --git a/llvm/test/Transforms/InstCombine/bitreverse-bswap-fold.ll b/llvm/test/Transforms/InstCombine/bitreverse-bswap-fold.ll
new file mode 100644
index 0000000000000..6c75a2a62c91d
--- /dev/null
+++ b/llvm/test/Transforms/InstCombine/bitreverse-bswap-fold.ll
@@ -0,0 +1,63 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes=instcombine < %s | FileCheck %s
+
+define i64 @fold_scalar_i64(i64 %0) {
+; CHECK-LABEL: define i64 @fold_scalar_i64(
+; CHECK-SAME: i64 [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast i64 [[TMP0]] to <8 x i8>
+; CHECK-NEXT:    [[TMP3:%.*]] = call <8 x i8> @llvm.bitreverse.v8i8(<8 x i8> [[TMP2]])
+; CHECK-NEXT:    [[T2:%.*]] = bitcast <8 x i8> [[TMP3]] to i64
+; CHECK-NEXT:    ret i64 [[T2]]
+;
+  %t1 = tail call i64 @llvm.bswap.i64(i64 %0)
+  %t2 = tail call i64 @llvm.bitreverse.i64(i64 %t1)
+  ret i64 %t2
+}
+
+define <2 x i64> @fold_vector_v2i64(<2 x i64> %0) {
+; CHECK-LABEL: define <2 x i64> @fold_vector_v2i64(
+; CHECK-SAME: <2 x i64> [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast <2 x i64> [[TMP0]] to <16 x i8>
+; CHECK-NEXT:    [[TMP3:%.*]] = call <16 x i8> @llvm.bitreverse.v16i8(<16 x i8> [[TMP2]])
+; CHECK-NEXT:    [[T2:%.*]] = bitcast <16 x i8> [[TMP3]] to <2 x i64>
+; CHECK-NEXT:    ret <2 x i64> [[T2]]
+;
+  %t1 = tail call <2 x i64> @llvm.bitreverse.v2i64(<2 x i64> %0)
+  %t2 = tail call <2 x i64> @llvm.bswap.v2i64(<2 x i64> %t1)
+  ret <2 x i64> %t2
+}
+
+define i32 @fold_scalar_i32(i32 %0) {
+; CHECK-LABEL: define i32 @fold_scalar_i32(
+; CHECK-SAME: i32 [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast i32 [[TMP0]] to <4 x i8>
+; CHECK-NEXT:    [[TMP3:%.*]] = call <4 x i8> @llvm.bitreverse.v4i8(<4 x i8> [[TMP2]])
+; CHECK-NEXT:    [[T2:%.*]] = bitcast <4 x i8> [[TMP3]] to i32
+; CHECK-NEXT:    ret i32 [[T2]]
+;
+  %t1 = tail call i32 @llvm.bswap.i32(i32 %0)
+  %t2 = tail call i32 @llvm.bitreverse.i32(i32 %t1)
+  ret i32 %t2
+}
+
+define <4 x i32> @fold_vector_v4i32(<4 x i32> %0) {
+; CHECK-LABEL: define <4 x i32> @fold_vector_v4i32(
+; CHECK-SAME: <4 x i32> [[TMP0:%.*]]) {
+; CHECK-NEXT:    [[TMP2:%.*]] = bitcast <4 x i32> [[TMP0]] to <16 x i8>
+; CHECK-NEXT:    [[TMP3:%.*]] = call <16 x i8> @llvm.bitreverse.v16i8(<16 x i8> [[TMP2]])
+; CHECK-NEXT:    [[T2:%.*]] = bitcast <16 x i8> [[TMP3]] to <4 x i32>
+; CHECK-NEXT:    ret <4 x i32> [[T2]]
+;
+  %t1 = tail call <4 x i32> @llvm.bswap.v4i32(<4 x i32> %0)
+  %t2 = tail call <4 x i32> @llvm.bitreverse.v4i32(<4 x i32> %t1)
+  ret <4 x i32> %t2
+}
+
+declare i64 @llvm.bswap.i64(i64)
+declare i64 @llvm.bitreverse.i64(i64)
+declare <2 x i64> @llvm.bswap.v2i64(<2 x i64>)
+declare <2 x i64> @llvm.bitreverse.v2i64(<2 x i64>)
+declare i32 @llvm.bswap.i32(i32)
+declare i32 @llvm.bitreverse.i32(i32)
+declare <4 x i32> @llvm.bswap.v4i32(<4 x i32>)
+declare <4 x i32> @llvm.bitreverse.v4i32(<4 x i32>)

``````````

</details>


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


More information about the llvm-commits mailing list