[llvm] [AArch64][ISel] Prefer SVE over NEON for fixed-width bitreverse (PR #196264)

via llvm-commits llvm-commits at lists.llvm.org
Thu May 7 02:15:53 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-aarch64

Author: Harry Ramsey (Harry-Ramsey)

<details>
<summary>Changes</summary>

This commit enables SVE instructions for bitreverse where no equivalent NEON instructions exist. This commit affects the following NEON datatypes; v4i16, v8i16, v2i32, v4i32, v1i64, v2i64.

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


2 Files Affected:

- (modified) llvm/lib/Target/AArch64/AArch64ISelLowering.cpp (+8-2) 
- (added) llvm/test/CodeGen/AArch64/sve-fixed-length-bitreverse.ll (+94) 


``````````diff
diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
index f5082b779d1db..78bbb8be2454b 100644
--- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
+++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp
@@ -1935,6 +1935,13 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM,
     setOperationAction(ISD::MUL, MVT::v1i64, Custom);
     setOperationAction(ISD::MUL, MVT::v2i64, Custom);
 
+    // Neon doesn't have dedicated instructions for vectors of this size but SVE
+    // does.
+    for (auto VT :
+         {MVT::v4i16, MVT::v8i16, MVT::v2i32, MVT::v4i32, MVT::v2i64}) {
+      setOperationAction(ISD::BITREVERSE, VT, Custom);
+    }
+
     // With SVE2 we can try lowering these to pairwise operations (e.g. smaxp).
     if (Subtarget->hasSVE2() || Subtarget->isStreamingSVEAvailable()) {
       setOperationAction(ISD::VECREDUCE_SMAX, MVT::v2i64, Custom);
@@ -11820,8 +11827,7 @@ SDValue AArch64TargetLowering::LowerBitreverse(SDValue Op,
   EVT VT = Op.getValueType();
 
   if (VT.isScalableVector() ||
-      useSVEForFixedLengthVectorVT(
-          VT, /*OverrideNEON=*/Subtarget->useSVEForFixedLengthVectors()))
+      useSVEForFixedLengthVectorVT(VT, /*OverrideNEON=*/true))
     return LowerToPredicatedOp(Op, DAG, AArch64ISD::BITREVERSE_MERGE_PASSTHRU);
 
   SDLoc DL(Op);
diff --git a/llvm/test/CodeGen/AArch64/sve-fixed-length-bitreverse.ll b/llvm/test/CodeGen/AArch64/sve-fixed-length-bitreverse.ll
new file mode 100644
index 0000000000000..e5308b2304509
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/sve-fixed-length-bitreverse.ll
@@ -0,0 +1,94 @@
+; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
+; RUN: llc -mtriple=aarch64-linux-gnu -mattr=+sve < %s | FileCheck %s -check-prefixes=CHECK-SVE
+
+; Do not use SVE, NEON has a dedicated instruction for fixed vector
+define <8 x i8> @test_v8i8(<8 x i8> %a) {
+; CHECK-SVE-LABEL: test_v8i8:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    rbit v0.8b, v0.8b
+; CHECK-SVE-NEXT:    ret
+  %r = call <8 x i8> @llvm.bitreverse(<8 x i8> %a)
+  ret <8 x i8> %r
+}
+
+; Do not use SVE, NEON has a dedicated instruction for fixed vector
+define <16 x i8> @test_v16i8(<16 x i8> %a) {
+; CHECK-SVE-LABEL: test_v16i8:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    rbit v0.16b, v0.16b
+; CHECK-SVE-NEXT:    ret
+  %r = call <16 x i8> @llvm.bitreverse(<16 x i8> %a)
+  ret <16 x i8> %r
+}
+
+define <4 x i16> @test_v4i16(<4 x i16> %a) {
+; CHECK-SVE-LABEL: test_v4i16:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.h, vl4
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.h, p0/m, z0.h
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <4 x i16> @llvm.bitreverse(<4 x i16> %a)
+  ret <4 x i16> %r
+}
+
+define <8 x i16> @test_v8i16(<8 x i16> %a) {
+; CHECK-SVE-LABEL: test_v8i16:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.h, vl8
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.h, p0/m, z0.h
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <8 x i16> @llvm.bitreverse(<8 x i16> %a)
+  ret <8 x i16> %r
+}
+
+define <2 x i32> @test_v2i32(<2 x i32> %a) {
+; CHECK-SVE-LABEL: test_v2i32:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.s, vl2
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.s, p0/m, z0.s
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <2 x i32> @llvm.bitreverse(<2 x i32> %a)
+  ret <2 x i32> %r
+}
+
+define <4 x i32> @test_v4i32(<4 x i32> %a) {
+; CHECK-SVE-LABEL: test_v4i32:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.s, vl4
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.s, p0/m, z0.s
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <4 x i32> @llvm.bitreverse(<4 x i32> %a)
+  ret <4 x i32> %r
+}
+
+define <1 x i64> @test_v1i64(<1 x i64> %a) {
+; CHECK-SVE-LABEL: test_v1i64:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl1
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.d, p0/m, z0.d
+; CHECK-SVE-NEXT:    // kill: def $d0 killed $d0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <1 x i64> @llvm.bitreverse(<1 x i64> %a)
+  ret <1 x i64> %r
+}
+
+define <2 x i64> @test_v2i64(<2 x i64> %a) {
+; CHECK-SVE-LABEL: test_v2i64:
+; CHECK-SVE:       // %bb.0:
+; CHECK-SVE-NEXT:    ptrue p0.d, vl2
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 def $z0
+; CHECK-SVE-NEXT:    rbit z0.d, p0/m, z0.d
+; CHECK-SVE-NEXT:    // kill: def $q0 killed $q0 killed $z0
+; CHECK-SVE-NEXT:    ret
+  %r = call <2 x i64> @llvm.bitreverse(<2 x i64> %a)
+  ret <2 x i64> %r
+}

``````````

</details>


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


More information about the llvm-commits mailing list