[llvm] [RISCV] Use bset+addi for (not (sll -1, X)). (PR #72549)

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Thu Nov 16 10:21:01 PST 2023


https://github.com/topperc created https://github.com/llvm/llvm-project/pull/72549

This is an alternative to #71420 that handles i32 on RV64 safely by pre-promoting the pattern in DAG combine.

>From 93bf2acfb5c2ef0249922bec38e8734a2fdeb452 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 16 Nov 2023 10:01:42 -0800
Subject: [PATCH 1/2] [RISCV] Add test cases for (not (sll -1, X)) for Zbs. NFC

We can use (ADDI (BSET X0, X), -1).
---
 llvm/test/CodeGen/RISCV/rv32zbs.ll | 81 ++++++++++++++++++++++++++++++
 llvm/test/CodeGen/RISCV/rv64zbs.ll | 50 ++++++++++++++++++
 2 files changed, 131 insertions(+)

diff --git a/llvm/test/CodeGen/RISCV/rv32zbs.ll b/llvm/test/CodeGen/RISCV/rv32zbs.ll
index 460d15991788238..55c27e69d38fd41 100644
--- a/llvm/test/CodeGen/RISCV/rv32zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv32zbs.ll
@@ -744,3 +744,84 @@ define i32 @or_i32_66901(i32 %a) nounwind {
   %or = or i32 %a, 66901
   ret i32 %or
 }
+
+define i32 @bset_trailing_ones_i32_mask(i32 %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i32_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sll a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %and = and i32 %a, 31
+  %shift = shl nsw i32 -1, %and
+  %not = xor i32 %shift, -1
+  ret i32 %not
+}
+
+define i32 @bset_trailing_ones_i32_no_mask(i32 %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i32_no_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sll a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %shift = shl nsw i32 -1, %a
+  %not = xor i32 %shift, -1
+  ret i32 %not
+}
+
+define i64 @bset_trailing_ones_i64_mask(i64 %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i64_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a2, -1
+; CHECK-NEXT:    andi a3, a0, 63
+; CHECK-NEXT:    addi a1, a3, -32
+; CHECK-NEXT:    sll a0, a2, a0
+; CHECK-NEXT:    bltz a1, .LBB43_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    sll a2, a2, a3
+; CHECK-NEXT:    j .LBB43_3
+; CHECK-NEXT:  .LBB43_2:
+; CHECK-NEXT:    not a2, a3
+; CHECK-NEXT:    lui a3, 524288
+; CHECK-NEXT:    addi a3, a3, -1
+; CHECK-NEXT:    srl a2, a3, a2
+; CHECK-NEXT:    or a2, a0, a2
+; CHECK-NEXT:  .LBB43_3:
+; CHECK-NEXT:    srai a1, a1, 31
+; CHECK-NEXT:    and a0, a1, a0
+; CHECK-NEXT:    not a1, a2
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %and = and i64 %a, 63
+  %shift = shl nsw i64 -1, %and
+  %not = xor i64 %shift, -1
+  ret i64 %not
+}
+
+define i64 @bset_trailing_ones_i64_no_mask(i64 %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i64_no_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    addi a2, a0, -32
+; CHECK-NEXT:    sll a1, a1, a0
+; CHECK-NEXT:    bltz a2, .LBB44_2
+; CHECK-NEXT:  # %bb.1:
+; CHECK-NEXT:    mv a0, a1
+; CHECK-NEXT:    j .LBB44_3
+; CHECK-NEXT:  .LBB44_2:
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    lui a3, 524288
+; CHECK-NEXT:    addi a3, a3, -1
+; CHECK-NEXT:    srl a0, a3, a0
+; CHECK-NEXT:    or a0, a1, a0
+; CHECK-NEXT:  .LBB44_3:
+; CHECK-NEXT:    srai a2, a2, 31
+; CHECK-NEXT:    and a2, a2, a1
+; CHECK-NEXT:    not a1, a0
+; CHECK-NEXT:    not a0, a2
+; CHECK-NEXT:    ret
+  %shift = shl nsw i64 -1, %a
+  %not = xor i64 %shift, -1
+  ret i64 %not
+}
diff --git a/llvm/test/CodeGen/RISCV/rv64zbs.ll b/llvm/test/CodeGen/RISCV/rv64zbs.ll
index b30b3c15196076b..bf822caa37341c7 100644
--- a/llvm/test/CodeGen/RISCV/rv64zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv64zbs.ll
@@ -1071,3 +1071,53 @@ define i64 @or_i64_66901(i64 %a) nounwind {
   %or = or i64 %a, 66901
   ret i64 %or
 }
+
+define signext i32 @bset_trailing_ones_i32_mask(i32 signext %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i32_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sllw a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %and = and i32 %a, 31
+  %shift = shl nsw i32 -1, %and
+  %not = xor i32 %shift, -1
+  ret i32 %not
+}
+
+define signext i32 @bset_trailing_ones_i32_no_mask(i32 signext %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i32_no_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sllw a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %shift = shl nsw i32 -1, %a
+  %not = xor i32 %shift, -1
+  ret i32 %not
+}
+
+define signext i64 @bset_trailing_ones_i64_mask(i64 signext %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i64_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sll a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %and = and i64 %a, 63
+  %shift = shl nsw i64 -1, %and
+  %not = xor i64 %shift, -1
+  ret i64 %not
+}
+
+define signext i64 @bset_trailing_ones_i64_no_mask(i64 signext %a) nounwind {
+; CHECK-LABEL: bset_trailing_ones_i64_no_mask:
+; CHECK:       # %bb.0:
+; CHECK-NEXT:    li a1, -1
+; CHECK-NEXT:    sll a0, a1, a0
+; CHECK-NEXT:    not a0, a0
+; CHECK-NEXT:    ret
+  %shift = shl nsw i64 -1, %a
+  %not = xor i64 %shift, -1
+  ret i64 %not
+}

>From c8cebfce007eb43b9f6e3f38f05348f0caced6b4 Mon Sep 17 00:00:00 2001
From: Craig Topper <craig.topper at sifive.com>
Date: Thu, 16 Nov 2023 10:03:52 -0800
Subject: [PATCH 2/2] [RISCV] Use bset+addi for (not (sll -1, X)).

This is an alternative to #71420 that handles i32 on RV64 safely
by pre-promoting the pattern in DAG combine.
---
 llvm/lib/Target/RISCV/RISCVISelLowering.cpp | 15 +++++
 llvm/lib/Target/RISCV/RISCVInstrInfoZb.td   |  2 +
 llvm/test/CodeGen/RISCV/rv32zbs.ll          | 36 ++++++----
 llvm/test/CodeGen/RISCV/rv64zbs.ll          | 73 ++++++++++++++-------
 4 files changed, 90 insertions(+), 36 deletions(-)

diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index fc0f59a09315b76..f89f300a4e9e50c 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -12362,6 +12362,21 @@ static SDValue performXORCombine(SDNode *N, SelectionDAG &DAG,
   SDValue N0 = N->getOperand(0);
   SDValue N1 = N->getOperand(1);
 
+  // Pre-promote (i32 (xor (shl -1, X), ~0)) on RV64 with Zbs so we can use
+  // (ADDI (BSET X0, X), -1). If we wait until/ type legalization, we'll create
+  // RISCVISD:::SLLW and we can't recover it to use a BSET instruction.
+  if (!RV64LegalI32 && Subtarget.is64Bit() && Subtarget.hasStdExtZbs() &&
+      N->getValueType(0) == MVT::i32 && isAllOnesConstant(N1) &&
+      N0.getOpcode() == ISD::SHL && isAllOnesConstant(N0.getOperand(0)) &&
+      !isa<ConstantSDNode>(N0.getOperand(1)) && N0.hasOneUse()) {
+    SDLoc DL(N);
+    SDValue Op0 = DAG.getNode(ISD::ANY_EXTEND, DL, MVT::i64, N0.getOperand(0));
+    SDValue Op1 = DAG.getNode(ISD::ZERO_EXTEND, DL, MVT::i64, N0.getOperand(1));
+    SDValue Shl = DAG.getNode(ISD::SHL, DL, MVT::i64, Op0, Op1);
+    SDValue And = DAG.getNOT(DL, Shl, MVT::i64);
+    return DAG.getNode(ISD::TRUNCATE, DL, MVT::i32, And);
+  }
+
   // fold (xor (sllw 1, x), -1) -> (rolw ~1, x)
   // NOTE: Assumes ROL being legal means ROLW is legal.
   const TargetLowering &TLI = DAG.getTargetLoweringInfo();
diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
index d1c29842b85d38a..f7f8560b57b5c2d 100644
--- a/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
+++ b/llvm/lib/Target/RISCV/RISCVInstrInfoZb.td
@@ -554,6 +554,8 @@ def : Pat<(XLenVT (and (shiftop<srl> GPR:$rs1, (XLenVT GPR:$rs2)), 1)),
 
 def : Pat<(XLenVT (shiftop<shl> 1, (XLenVT GPR:$rs2))),
           (BSET (XLenVT X0), GPR:$rs2)>;
+def : Pat<(XLenVT (not (shiftop<shl> -1, (XLenVT GPR:$rs2)))),
+          (ADDI (BSET (XLenVT X0), GPR:$rs2), -1)>;
 
 def : Pat<(XLenVT (and GPR:$rs1, BCLRMask:$mask)),
           (BCLRI GPR:$rs1, BCLRMask:$mask)>;
diff --git a/llvm/test/CodeGen/RISCV/rv32zbs.ll b/llvm/test/CodeGen/RISCV/rv32zbs.ll
index 55c27e69d38fd41..ccda8f4e5dd0595 100644
--- a/llvm/test/CodeGen/RISCV/rv32zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv32zbs.ll
@@ -746,12 +746,18 @@ define i32 @or_i32_66901(i32 %a) nounwind {
 }
 
 define i32 @bset_trailing_ones_i32_mask(i32 %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i32_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sll a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV32I-LABEL: bset_trailing_ones_i32_mask:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a1, -1
+; RV32I-NEXT:    sll a0, a1, a0
+; RV32I-NEXT:    not a0, a0
+; RV32I-NEXT:    ret
+;
+; RV32ZBS-LABEL: bset_trailing_ones_i32_mask:
+; RV32ZBS:       # %bb.0:
+; RV32ZBS-NEXT:    bset a0, zero, a0
+; RV32ZBS-NEXT:    addi a0, a0, -1
+; RV32ZBS-NEXT:    ret
   %and = and i32 %a, 31
   %shift = shl nsw i32 -1, %and
   %not = xor i32 %shift, -1
@@ -759,12 +765,18 @@ define i32 @bset_trailing_ones_i32_mask(i32 %a) nounwind {
 }
 
 define i32 @bset_trailing_ones_i32_no_mask(i32 %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i32_no_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sll a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV32I-LABEL: bset_trailing_ones_i32_no_mask:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a1, -1
+; RV32I-NEXT:    sll a0, a1, a0
+; RV32I-NEXT:    not a0, a0
+; RV32I-NEXT:    ret
+;
+; RV32ZBS-LABEL: bset_trailing_ones_i32_no_mask:
+; RV32ZBS:       # %bb.0:
+; RV32ZBS-NEXT:    bset a0, zero, a0
+; RV32ZBS-NEXT:    addi a0, a0, -1
+; RV32ZBS-NEXT:    ret
   %shift = shl nsw i32 -1, %a
   %not = xor i32 %shift, -1
   ret i32 %not
diff --git a/llvm/test/CodeGen/RISCV/rv64zbs.ll b/llvm/test/CodeGen/RISCV/rv64zbs.ll
index bf822caa37341c7..016b0924eaf1993 100644
--- a/llvm/test/CodeGen/RISCV/rv64zbs.ll
+++ b/llvm/test/CodeGen/RISCV/rv64zbs.ll
@@ -1073,12 +1073,19 @@ define i64 @or_i64_66901(i64 %a) nounwind {
 }
 
 define signext i32 @bset_trailing_ones_i32_mask(i32 signext %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i32_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sllw a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bset_trailing_ones_i32_mask:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a1, -1
+; RV64I-NEXT:    sllw a0, a1, a0
+; RV64I-NEXT:    not a0, a0
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bset_trailing_ones_i32_mask:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    andi a0, a0, 31
+; RV64ZBS-NEXT:    bset a0, zero, a0
+; RV64ZBS-NEXT:    addi a0, a0, -1
+; RV64ZBS-NEXT:    ret
   %and = and i32 %a, 31
   %shift = shl nsw i32 -1, %and
   %not = xor i32 %shift, -1
@@ -1086,24 +1093,36 @@ define signext i32 @bset_trailing_ones_i32_mask(i32 signext %a) nounwind {
 }
 
 define signext i32 @bset_trailing_ones_i32_no_mask(i32 signext %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i32_no_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sllw a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bset_trailing_ones_i32_no_mask:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a1, -1
+; RV64I-NEXT:    sllw a0, a1, a0
+; RV64I-NEXT:    not a0, a0
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bset_trailing_ones_i32_no_mask:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    bset a0, zero, a0
+; RV64ZBS-NEXT:    addiw a0, a0, -1
+; RV64ZBS-NEXT:    ret
   %shift = shl nsw i32 -1, %a
   %not = xor i32 %shift, -1
   ret i32 %not
 }
 
 define signext i64 @bset_trailing_ones_i64_mask(i64 signext %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i64_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sll a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bset_trailing_ones_i64_mask:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a1, -1
+; RV64I-NEXT:    sll a0, a1, a0
+; RV64I-NEXT:    not a0, a0
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bset_trailing_ones_i64_mask:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    bset a0, zero, a0
+; RV64ZBS-NEXT:    addi a0, a0, -1
+; RV64ZBS-NEXT:    ret
   %and = and i64 %a, 63
   %shift = shl nsw i64 -1, %and
   %not = xor i64 %shift, -1
@@ -1111,12 +1130,18 @@ define signext i64 @bset_trailing_ones_i64_mask(i64 signext %a) nounwind {
 }
 
 define signext i64 @bset_trailing_ones_i64_no_mask(i64 signext %a) nounwind {
-; CHECK-LABEL: bset_trailing_ones_i64_no_mask:
-; CHECK:       # %bb.0:
-; CHECK-NEXT:    li a1, -1
-; CHECK-NEXT:    sll a0, a1, a0
-; CHECK-NEXT:    not a0, a0
-; CHECK-NEXT:    ret
+; RV64I-LABEL: bset_trailing_ones_i64_no_mask:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a1, -1
+; RV64I-NEXT:    sll a0, a1, a0
+; RV64I-NEXT:    not a0, a0
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bset_trailing_ones_i64_no_mask:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    bset a0, zero, a0
+; RV64ZBS-NEXT:    addi a0, a0, -1
+; RV64ZBS-NEXT:    ret
   %shift = shl nsw i64 -1, %a
   %not = xor i64 %shift, -1
   ret i64 %not



More information about the llvm-commits mailing list