[llvm] [RISCV] Select signed bitfield extracts for XAndesPerf (PR #142303)

Jim Lin via llvm-commits llvm-commits at lists.llvm.org
Sun Jun 1 01:17:31 PDT 2025


https://github.com/tclin914 created https://github.com/llvm/llvm-project/pull/142303

The XAndesPerf extension includes signed bitfield extraction
instruction `NDS.BFOS`, which can extract the bits from LSB to MSB,
places them starting at bit 0, and sign-extends the result.

The testcase includes the two patterns that can be selected as
signed bitfield extracts: `ashr+shl` and `ashr+sext_inreg`

>From deb0edce4b0459a7c86045af4dcfe27ef63160ac Mon Sep 17 00:00:00 2001
From: Jim Lin <jim at andestech.com>
Date: Sun, 1 Jun 2025 10:33:58 +0800
Subject: [PATCH 1/2] [RISCV] Pre-commit

---
 llvm/test/CodeGen/RISCV/rv32xandesperf.ll | 83 +++++++++++++++++++++++
 llvm/test/CodeGen/RISCV/rv64xandesperf.ll | 66 ++++++++++++++++++
 2 files changed, 149 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/rv32xandesperf.ll b/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
index 72dddddf9f382..60d4ce58ecaa9 100644
--- a/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
+++ b/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
@@ -70,6 +70,89 @@ define i64 @bfoz_from_lshr_and_i64(i64 %x) {
   ret i64 %shifted
 }
 
+define i32 @bfos_from_ashr_shl_i32(i32 %x) {
+; CHECK-LABEL: bfos_from_ashr_shl_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 8
+; CHECK-NEXT:    srai a0, a0, 24
+; CHECK-NEXT:    ret
+  %shl = shl i32 %x, 8
+  %ashr = ashr i32 %shl, 24
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_shl_i64(i64 %x) {
+; CHECK-LABEL: bfos_from_ashr_shl_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    # kill: def $x12 killed $x11
+; CHECK-NEXT:    # kill: def $x12 killed $x10
+; CHECK-NEXT:    srli a2, a0, 24
+; CHECK-NEXT:    slli a1, a1, 8
+; CHECK-NEXT:    or a2, a1, a2
+; CHECK-NEXT:    slli a2, a2, 8
+; CHECK-NEXT:    nds.bfoz a0, a0, 23, 16
+; CHECK-NEXT:    or a0, a0, a2
+; CHECK-NEXT:    srai a1, a1, 24
+; CHECK-NEXT:    ret
+  %shl = shl i64 %x, 8
+  %ashr = ashr i64 %shl, 24
+  ret i64 %ashr
+}
+
+define i32 @bfos_from_ashr_sexti8_i32(i8 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti8_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    # kill: def $x11 killed $x10
+; CHECK-NEXT:    slli a0, a0, 24
+; CHECK-NEXT:    srai a0, a0, 29
+; CHECK-NEXT:    ret
+  %sext = sext i8 %x to i32
+  %ashr = ashr i32 %sext, 5
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_sexti8_i64(i8 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti8_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    # kill: def $x11 killed $x10
+; CHECK-NEXT:    nds.bfos a0, a0, 7, 0
+; CHECK-NEXT:    srai a1, a0, 31
+; CHECK-NEXT:    srli a0, a0, 5
+; CHECK-NEXT:    slli a2, a1, 27
+; CHECK-NEXT:    or a0, a0, a2
+; CHECK-NEXT:    ret
+  %sext = sext i8 %x to i64
+  %ashr = ashr i64 %sext, 5
+  ret i64 %ashr
+}
+
+define i32 @bfos_from_ashr_sexti16_i32(i16 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti16_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    # kill: def $x11 killed $x10
+; CHECK-NEXT:    slli a0, a0, 16
+; CHECK-NEXT:    srai a0, a0, 27
+; CHECK-NEXT:    ret
+  %sext = sext i16 %x to i32
+  %ashr = ashr i32 %sext, 11
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_sexti16_i64(i16 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti16_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    # kill: def $x11 killed $x10
+; CHECK-NEXT:    nds.bfos a0, a0, 15, 0
+; CHECK-NEXT:    srai a1, a0, 31
+; CHECK-NEXT:    srli a0, a0, 11
+; CHECK-NEXT:    slli a2, a1, 21
+; CHECK-NEXT:    or a0, a0, a2
+; CHECK-NEXT:    ret
+  %sext = sext i16 %x to i64
+  %ashr = ashr i64 %sext, 11
+  ret i64 %ashr
+}
+
 define i32 @sexti1_i32(i32 %a) {
 ; CHECK-LABEL: sexti1_i32:
 ; CHECK:       # %bb.0:
diff --git a/llvm/test/CodeGen/RISCV/rv64xandesperf.ll b/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
index 13c2234071eb1..6c83d46647c90 100644
--- a/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
+++ b/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
@@ -60,6 +60,72 @@ define i64 @bfoz_from_lshr_and_i64(i64 %x) {
   ret i64 %shifted
 }
 
+define i32 @bfos_from_ashr_shl_i32(i32 %x) {
+; CHECK-LABEL: bfos_from_ashr_shl_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 40
+; CHECK-NEXT:    srai a0, a0, 56
+; CHECK-NEXT:    ret
+  %shl = shl i32 %x, 8
+  %ashr = ashr i32 %shl, 24
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_shl_i64(i64 %x) {
+; CHECK-LABEL: bfos_from_ashr_shl_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 8
+; CHECK-NEXT:    srai a0, a0, 24
+; CHECK-NEXT:    ret
+  %shl = shl i64 %x, 8
+  %ashr = ashr i64 %shl, 24
+  ret i64 %ashr
+}
+
+define i32 @bfos_from_ashr_sexti8_i32(i8 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti8_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 56
+; CHECK-NEXT:    srai a0, a0, 61
+; CHECK-NEXT:    ret
+  %sext = sext i8 %x to i32
+  %ashr = ashr i32 %sext, 5
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_sexti8_i64(i8 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti8_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 56
+; CHECK-NEXT:    srai a0, a0, 61
+; CHECK-NEXT:    ret
+  %sext = sext i8 %x to i64
+  %ashr = ashr i64 %sext, 5
+  ret i64 %ashr
+}
+
+define i32 @bfos_from_ashr_sexti16_i32(i16 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti16_i32:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 48
+; CHECK-NEXT:    srai a0, a0, 59
+; CHECK-NEXT:    ret
+  %sext = sext i16 %x to i32
+  %ashr = ashr i32 %sext, 11
+  ret i32 %ashr
+}
+
+define i64 @bfos_from_ashr_sexti16_i64(i16 %x) {
+; CHECK-LABEL: bfos_from_ashr_sexti16_i64:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    slli a0, a0, 48
+; CHECK-NEXT:    srai a0, a0, 59
+; CHECK-NEXT:    ret
+  %sext = sext i16 %x to i64
+  %ashr = ashr i64 %sext, 11
+  ret i64 %ashr
+}
+
 define signext i32 @sexti1_i32(i32 signext %a) {
 ; CHECK-LABEL: sexti1_i32:
 ; CHECK:       # %bb.0:

>From 36a3d516303e1d7799ac7c23ad9a8b6cf9429eca Mon Sep 17 00:00:00 2001
From: Jim Lin <jim at andestech.com>
Date: Sun, 1 Jun 2025 10:34:29 +0800
Subject: [PATCH 2/2] [RISCV] Select signed bitfield extracts for XAndesPerf

The XAndesPerf extension includes signed bitfield extraction
instruction `NDS.BFOS`, which can extract the bits from LSB to MSB,
places them starting at bit 0, and sign-extends the result.

The testcase includes the two patterns that can be selected as
signed bitfield extracts: `ashr+shl` and `ashr+sext_inreg`
---
 llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp |  8 +++++---
 llvm/test/CodeGen/RISCV/rv32xandesperf.ll   |  9 +++------
 llvm/test/CodeGen/RISCV/rv64xandesperf.ll   | 18 ++++++------------
 3 files changed, 14 insertions(+), 21 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
index 2de22ee165aea..4f6aa41d1e03b 100644
--- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
@@ -601,8 +601,8 @@ bool RISCVDAGToDAGISel::tryShrinkShlLogicImm(SDNode *Node) {
 }
 
 bool RISCVDAGToDAGISel::trySignedBitfieldExtract(SDNode *Node) {
-  // Only supported with XTHeadBb at the moment.
-  if (!Subtarget->hasVendorXTHeadBb())
+  // Only supported with XTHeadBb/XAndesPerf at the moment.
+  if (!Subtarget->hasVendorXTHeadBb() && !Subtarget->hasVendorXAndesPerf())
     return false;
 
   auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
@@ -615,7 +615,9 @@ bool RISCVDAGToDAGISel::trySignedBitfieldExtract(SDNode *Node) {
 
   auto BitfieldExtract = [&](SDValue N0, unsigned Msb, unsigned Lsb, SDLoc DL,
                              MVT VT) {
-    return CurDAG->getMachineNode(RISCV::TH_EXT, DL, VT, N0.getOperand(0),
+    unsigned Opc =
+        Subtarget->hasVendorXTHeadBb() ? RISCV::TH_EXT : RISCV::NDS_BFOS;
+    return CurDAG->getMachineNode(Opc, DL, VT, N0.getOperand(0),
                                   CurDAG->getTargetConstant(Msb, DL, VT),
                                   CurDAG->getTargetConstant(Lsb, DL, VT));
   };
diff --git a/llvm/test/CodeGen/RISCV/rv32xandesperf.ll b/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
index 60d4ce58ecaa9..af353a1e541fe 100644
--- a/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
+++ b/llvm/test/CodeGen/RISCV/rv32xandesperf.ll
@@ -73,8 +73,7 @@ define i64 @bfoz_from_lshr_and_i64(i64 %x) {
 define i32 @bfos_from_ashr_shl_i32(i32 %x) {
 ; CHECK-LABEL: bfos_from_ashr_shl_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 8
-; CHECK-NEXT:    srai a0, a0, 24
+; CHECK-NEXT:    nds.bfos a0, a0, 23, 16
 ; CHECK-NEXT:    ret
   %shl = shl i32 %x, 8
   %ashr = ashr i32 %shl, 24
@@ -103,8 +102,7 @@ define i32 @bfos_from_ashr_sexti8_i32(i8 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti8_i32:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    # kill: def $x11 killed $x10
-; CHECK-NEXT:    slli a0, a0, 24
-; CHECK-NEXT:    srai a0, a0, 29
+; CHECK-NEXT:    nds.bfos a0, a0, 7, 5
 ; CHECK-NEXT:    ret
   %sext = sext i8 %x to i32
   %ashr = ashr i32 %sext, 5
@@ -130,8 +128,7 @@ define i32 @bfos_from_ashr_sexti16_i32(i16 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti16_i32:
 ; CHECK:       # %bb.0:
 ; CHECK-NEXT:    # kill: def $x11 killed $x10
-; CHECK-NEXT:    slli a0, a0, 16
-; CHECK-NEXT:    srai a0, a0, 27
+; CHECK-NEXT:    nds.bfos a0, a0, 15, 11
 ; CHECK-NEXT:    ret
   %sext = sext i16 %x to i32
   %ashr = ashr i32 %sext, 11
diff --git a/llvm/test/CodeGen/RISCV/rv64xandesperf.ll b/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
index 6c83d46647c90..260d30be686dc 100644
--- a/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
+++ b/llvm/test/CodeGen/RISCV/rv64xandesperf.ll
@@ -63,8 +63,7 @@ define i64 @bfoz_from_lshr_and_i64(i64 %x) {
 define i32 @bfos_from_ashr_shl_i32(i32 %x) {
 ; CHECK-LABEL: bfos_from_ashr_shl_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 40
-; CHECK-NEXT:    srai a0, a0, 56
+; CHECK-NEXT:    nds.bfos a0, a0, 23, 16
 ; CHECK-NEXT:    ret
   %shl = shl i32 %x, 8
   %ashr = ashr i32 %shl, 24
@@ -74,8 +73,7 @@ define i32 @bfos_from_ashr_shl_i32(i32 %x) {
 define i64 @bfos_from_ashr_shl_i64(i64 %x) {
 ; CHECK-LABEL: bfos_from_ashr_shl_i64:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 8
-; CHECK-NEXT:    srai a0, a0, 24
+; CHECK-NEXT:    nds.bfos a0, a0, 55, 16
 ; CHECK-NEXT:    ret
   %shl = shl i64 %x, 8
   %ashr = ashr i64 %shl, 24
@@ -85,8 +83,7 @@ define i64 @bfos_from_ashr_shl_i64(i64 %x) {
 define i32 @bfos_from_ashr_sexti8_i32(i8 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti8_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 56
-; CHECK-NEXT:    srai a0, a0, 61
+; CHECK-NEXT:    nds.bfos a0, a0, 7, 5
 ; CHECK-NEXT:    ret
   %sext = sext i8 %x to i32
   %ashr = ashr i32 %sext, 5
@@ -96,8 +93,7 @@ define i32 @bfos_from_ashr_sexti8_i32(i8 %x) {
 define i64 @bfos_from_ashr_sexti8_i64(i8 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti8_i64:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 56
-; CHECK-NEXT:    srai a0, a0, 61
+; CHECK-NEXT:    nds.bfos a0, a0, 7, 5
 ; CHECK-NEXT:    ret
   %sext = sext i8 %x to i64
   %ashr = ashr i64 %sext, 5
@@ -107,8 +103,7 @@ define i64 @bfos_from_ashr_sexti8_i64(i8 %x) {
 define i32 @bfos_from_ashr_sexti16_i32(i16 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti16_i32:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 48
-; CHECK-NEXT:    srai a0, a0, 59
+; CHECK-NEXT:    nds.bfos a0, a0, 15, 11
 ; CHECK-NEXT:    ret
   %sext = sext i16 %x to i32
   %ashr = ashr i32 %sext, 11
@@ -118,8 +113,7 @@ define i32 @bfos_from_ashr_sexti16_i32(i16 %x) {
 define i64 @bfos_from_ashr_sexti16_i64(i16 %x) {
 ; CHECK-LABEL: bfos_from_ashr_sexti16_i64:
 ; CHECK:       # %bb.0:
-; CHECK-NEXT:    slli a0, a0, 48
-; CHECK-NEXT:    srai a0, a0, 59
+; CHECK-NEXT:    nds.bfos a0, a0, 15, 11
 ; CHECK-NEXT:    ret
   %sext = sext i16 %x to i64
   %ashr = ashr i64 %sext, 11



More information about the llvm-commits mailing list