[llvm] [TargetLowering][RISCV] Introduce shouldFoldSelectWithSingleBitTest and RISC-V implement. (PR #72978)

via llvm-commits llvm-commits at lists.llvm.org
Tue Nov 21 03:34:54 PST 2023


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-backend-risc-v

Author: Yeting Kuo (yetingk)

<details>
<summary>Changes</summary>

DAGCombiner folds (select_cc seteq (and x, y), 0, 0, A) to (and (sra (shl x)) A) where y has a single bit set. Previously, DAGCombiner relies on `shouldAvoidTransformToShift` to decide when to do the combine, but `shouldAvoidTransformToShift` is only about shift cost. This patch introuduces a specific hook to decide when to do the combine and disable the combine when Zicond enabled and AndMask <= 1024.

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


6 Files Affected:

- (modified) llvm/include/llvm/CodeGen/TargetLowering.h (+8) 
- (modified) llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp (+2-2) 
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.cpp (+7) 
- (modified) llvm/lib/Target/RISCV/RISCVISelLowering.h (+3) 
- (modified) llvm/test/CodeGen/RISCV/condops.ll (+100) 
- (modified) llvm/test/CodeGen/RISCV/select.ll (+123-65) 


``````````diff
diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h
index 9ebcc28c38ae672..fbd90a0b75a744e 100644
--- a/llvm/include/llvm/CodeGen/TargetLowering.h
+++ b/llvm/include/llvm/CodeGen/TargetLowering.h
@@ -3259,6 +3259,14 @@ class TargetLoweringBase {
     return false;
   }
 
+  // Should we fold (select_cc seteq (and x, y), 0, 0, A) -> (and (sra (shl x))
+  // A) where y has a single bit set?
+  virtual bool shouldFoldSelectWithSingleBitTest(EVT VT,
+                                                 const APInt &AndMask) const {
+    unsigned ShCt = AndMask.getBitWidth() - 1;
+    return !shouldAvoidTransformToShift(VT, ShCt);
+  }
+
   /// Does this target require the clearing of high-order bits in a register
   /// passed to the fp16 to fp conversion library function.
   virtual bool shouldKeepZExtForFP16Conv() const { return false; }
diff --git a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
index 2fc9a2866c32dba..83a1a8b3181cc40 100644
--- a/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
+++ b/llvm/lib/CodeGen/SelectionDAG/DAGCombiner.cpp
@@ -27252,8 +27252,8 @@ SDValue DAGCombiner::SimplifySelectCC(const SDLoc &DL, SDValue N0, SDValue N1,
     if (ConstAndRHS && ConstAndRHS->getAPIntValue().popcount() == 1) {
       // Shift the tested bit over the sign bit.
       const APInt &AndMask = ConstAndRHS->getAPIntValue();
-      unsigned ShCt = AndMask.getBitWidth() - 1;
-      if (!TLI.shouldAvoidTransformToShift(VT, ShCt)) {
+      if (TLI.shouldFoldSelectWithSingleBitTest(VT, AndMask)) {
+        unsigned ShCt = AndMask.getBitWidth() - 1;
         SDValue ShlAmt =
             DAG.getConstant(AndMask.countl_zero(), SDLoc(AndLHS),
                             getShiftAmountTy(AndLHS.getValueType()));
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index 26190337eb3bd1b..d3a1f5a9dd9e00c 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -19673,6 +19673,13 @@ RISCVTargetLowering::BuildSDIVPow2(SDNode *N, const APInt &Divisor,
     return SDValue();
   return TargetLowering::buildSDIVPow2WithCMov(N, Divisor, DAG, Created);
 }
+
+bool RISCVTargetLowering::shouldFoldSelectWithSingleBitTest(
+    EVT VT, const APInt &AndMask) const {
+  if (Subtarget.hasStdExtZicond() || Subtarget.hasVendorXVentanaCondOps())
+    return AndMask.ugt(1024);
+  return TargetLowering::shouldFoldSelectWithSingleBitTest(VT, AndMask);
+}
 namespace llvm::RISCVVIntrinsicsTable {
 
 #define GET_RISCVVIntrinsicsTable_IMPL
diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 8f3ff4be22a2d1b..e10db7d441ef25e 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -959,6 +959,9 @@ class RISCVTargetLowering : public TargetLowering {
 
   SDValue BuildSDIVPow2(SDNode *N, const APInt &Divisor, SelectionDAG &DAG,
                         SmallVectorImpl<SDNode *> &Created) const override;
+
+  bool shouldFoldSelectWithSingleBitTest(EVT VT,
+                                         const APInt &AndMask) const override;
 };
 
 namespace RISCV {
diff --git a/llvm/test/CodeGen/RISCV/condops.ll b/llvm/test/CodeGen/RISCV/condops.ll
index c405b0ae1717847..b9912c6ccfb98cd 100644
--- a/llvm/test/CodeGen/RISCV/condops.ll
+++ b/llvm/test/CodeGen/RISCV/condops.ll
@@ -3533,3 +3533,103 @@ define signext i16 @numsignbits(i16 signext %0, i16 signext %1, i16 signext %2,
 }
 
 declare void @bat(i16 signext)
+
+define i64 @single_bit(i64 %x) {
+; RV32I-LABEL: single_bit:
+; RV32I:       # %bb.0: # %entry
+; RV32I-NEXT:    slli a2, a0, 21
+; RV32I-NEXT:    srai a2, a2, 31
+; RV32I-NEXT:    and a0, a2, a0
+; RV32I-NEXT:    and a1, a2, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: single_bit:
+; RV64I:       # %bb.0: # %entry
+; RV64I-NEXT:    slli a1, a0, 53
+; RV64I-NEXT:    srai a1, a1, 63
+; RV64I-NEXT:    and a0, a1, a0
+; RV64I-NEXT:    ret
+;
+; RV64XVENTANACONDOPS-LABEL: single_bit:
+; RV64XVENTANACONDOPS:       # %bb.0: # %entry
+; RV64XVENTANACONDOPS-NEXT:    andi a1, a0, 1024
+; RV64XVENTANACONDOPS-NEXT:    vt.maskc a0, a0, a1
+; RV64XVENTANACONDOPS-NEXT:    ret
+;
+; RV64XTHEADCONDMOV-LABEL: single_bit:
+; RV64XTHEADCONDMOV:       # %bb.0: # %entry
+; RV64XTHEADCONDMOV-NEXT:    slli a1, a0, 53
+; RV64XTHEADCONDMOV-NEXT:    srai a1, a1, 63
+; RV64XTHEADCONDMOV-NEXT:    and a0, a1, a0
+; RV64XTHEADCONDMOV-NEXT:    ret
+;
+; RV32ZICOND-LABEL: single_bit:
+; RV32ZICOND:       # %bb.0: # %entry
+; RV32ZICOND-NEXT:    andi a2, a0, 1024
+; RV32ZICOND-NEXT:    czero.eqz a0, a0, a2
+; RV32ZICOND-NEXT:    czero.eqz a1, a1, a2
+; RV32ZICOND-NEXT:    ret
+;
+; RV64ZICOND-LABEL: single_bit:
+; RV64ZICOND:       # %bb.0: # %entry
+; RV64ZICOND-NEXT:    andi a1, a0, 1024
+; RV64ZICOND-NEXT:    czero.eqz a0, a0, a1
+; RV64ZICOND-NEXT:    ret
+entry:
+  %and = and i64 %x, 1024
+  %tobool.not = icmp eq i64 %and, 0
+  %cond = select i1 %tobool.not, i64 0, i64 %x
+  ret i64 %cond
+}
+
+; Test to fold select with single bit check to (and (sra (shl x))).
+define i64 @single_bit2(i64 %x) {
+; RV32I-LABEL: single_bit2:
+; RV32I:       # %bb.0: # %entry
+; RV32I-NEXT:    slli a2, a0, 20
+; RV32I-NEXT:    srai a2, a2, 31
+; RV32I-NEXT:    and a0, a2, a0
+; RV32I-NEXT:    and a1, a2, a1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: single_bit2:
+; RV64I:       # %bb.0: # %entry
+; RV64I-NEXT:    slli a1, a0, 52
+; RV64I-NEXT:    srai a1, a1, 63
+; RV64I-NEXT:    and a0, a1, a0
+; RV64I-NEXT:    ret
+;
+; RV64XVENTANACONDOPS-LABEL: single_bit2:
+; RV64XVENTANACONDOPS:       # %bb.0: # %entry
+; RV64XVENTANACONDOPS-NEXT:    slli a1, a0, 52
+; RV64XVENTANACONDOPS-NEXT:    srai a1, a1, 63
+; RV64XVENTANACONDOPS-NEXT:    and a0, a1, a0
+; RV64XVENTANACONDOPS-NEXT:    ret
+;
+; RV64XTHEADCONDMOV-LABEL: single_bit2:
+; RV64XTHEADCONDMOV:       # %bb.0: # %entry
+; RV64XTHEADCONDMOV-NEXT:    slli a1, a0, 52
+; RV64XTHEADCONDMOV-NEXT:    srai a1, a1, 63
+; RV64XTHEADCONDMOV-NEXT:    and a0, a1, a0
+; RV64XTHEADCONDMOV-NEXT:    ret
+;
+; RV32ZICOND-LABEL: single_bit2:
+; RV32ZICOND:       # %bb.0: # %entry
+; RV32ZICOND-NEXT:    slli a2, a0, 20
+; RV32ZICOND-NEXT:    srai a2, a2, 31
+; RV32ZICOND-NEXT:    and a0, a2, a0
+; RV32ZICOND-NEXT:    and a1, a2, a1
+; RV32ZICOND-NEXT:    ret
+;
+; RV64ZICOND-LABEL: single_bit2:
+; RV64ZICOND:       # %bb.0: # %entry
+; RV64ZICOND-NEXT:    slli a1, a0, 52
+; RV64ZICOND-NEXT:    srai a1, a1, 63
+; RV64ZICOND-NEXT:    and a0, a1, a0
+; RV64ZICOND-NEXT:    ret
+entry:
+  %and = and i64 %x, 2048
+  %tobool.not = icmp eq i64 %and, 0
+  %cond = select i1 %tobool.not, i64 0, i64 %x
+  ret i64 %cond
+}
diff --git a/llvm/test/CodeGen/RISCV/select.ll b/llvm/test/CodeGen/RISCV/select.ll
index d4a6e9e9dbb4678..7fa27a307757d09 100644
--- a/llvm/test/CodeGen/RISCV/select.ll
+++ b/llvm/test/CodeGen/RISCV/select.ll
@@ -1,26 +1,42 @@
 ; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
-; RUN: llc -mtriple=riscv32 -mattr=+m -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECK32,RV32IM %s
-; RUN: llc -mtriple=riscv64 -mattr=+m -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECK64,RV64IM %s
-; RUN: llc -mtriple=riscv64 -mattr=+m,+xventanacondops -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECK64,RV64IMXVTCONDOPS %s
-; RUN: llc -mtriple=riscv32 -mattr=+m,+experimental-zicond -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECK32,CHECKZICOND,RV32IMZICOND %s
-; RUN: llc -mtriple=riscv64 -mattr=+m,+experimental-zicond -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECK64,CHECKZICOND,RV64IMZICOND %s
+; RUN: llc -mtriple=riscv32 -mattr=+m -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,RV32IM %s
+; RUN: llc -mtriple=riscv64 -mattr=+m -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,RV64IM %s
+; RUN: llc -mtriple=riscv64 -mattr=+m,+xventanacondops -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,RV64IMXVTCONDOPS %s
+; RUN: llc -mtriple=riscv32 -mattr=+m,+experimental-zicond -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECKZICOND,RV32IMZICOND %s
+; RUN: llc -mtriple=riscv64 -mattr=+m,+experimental-zicond -verify-machineinstrs < %s | FileCheck --check-prefixes=CHECK,CHECKZICOND,RV64IMZICOND %s
 
 define i16 @select_xor_1(i16 %A, i8 %cond) {
-; CHECK32-LABEL: select_xor_1:
-; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    slli a1, a1, 31
-; CHECK32-NEXT:    srai a1, a1, 31
-; CHECK32-NEXT:    andi a1, a1, 43
-; CHECK32-NEXT:    xor a0, a0, a1
-; CHECK32-NEXT:    ret
-;
-; CHECK64-LABEL: select_xor_1:
-; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    slli a1, a1, 63
-; CHECK64-NEXT:    srai a1, a1, 63
-; CHECK64-NEXT:    andi a1, a1, 43
-; CHECK64-NEXT:    xor a0, a0, a1
-; CHECK64-NEXT:    ret
+; RV32IM-LABEL: select_xor_1:
+; RV32IM:       # %bb.0: # %entry
+; RV32IM-NEXT:    slli a1, a1, 31
+; RV32IM-NEXT:    srai a1, a1, 31
+; RV32IM-NEXT:    andi a1, a1, 43
+; RV32IM-NEXT:    xor a0, a0, a1
+; RV32IM-NEXT:    ret
+;
+; RV64IM-LABEL: select_xor_1:
+; RV64IM:       # %bb.0: # %entry
+; RV64IM-NEXT:    slli a1, a1, 63
+; RV64IM-NEXT:    srai a1, a1, 63
+; RV64IM-NEXT:    andi a1, a1, 43
+; RV64IM-NEXT:    xor a0, a0, a1
+; RV64IM-NEXT:    ret
+;
+; RV64IMXVTCONDOPS-LABEL: select_xor_1:
+; RV64IMXVTCONDOPS:       # %bb.0: # %entry
+; RV64IMXVTCONDOPS-NEXT:    andi a1, a1, 1
+; RV64IMXVTCONDOPS-NEXT:    li a2, 43
+; RV64IMXVTCONDOPS-NEXT:    vt.maskc a1, a2, a1
+; RV64IMXVTCONDOPS-NEXT:    xor a0, a0, a1
+; RV64IMXVTCONDOPS-NEXT:    ret
+;
+; CHECKZICOND-LABEL: select_xor_1:
+; CHECKZICOND:       # %bb.0: # %entry
+; CHECKZICOND-NEXT:    andi a1, a1, 1
+; CHECKZICOND-NEXT:    li a2, 43
+; CHECKZICOND-NEXT:    czero.eqz a1, a2, a1
+; CHECKZICOND-NEXT:    xor a0, a0, a1
+; CHECKZICOND-NEXT:    ret
 entry:
  %and = and i8 %cond, 1
  %cmp10 = icmp eq i8 %and, 0
@@ -72,21 +88,35 @@ entry:
 }
 
 define i32 @select_xor_2(i32 %A, i32 %B, i8 %cond) {
-; CHECK32-LABEL: select_xor_2:
-; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    slli a2, a2, 31
-; CHECK32-NEXT:    srai a2, a2, 31
-; CHECK32-NEXT:    and a1, a2, a1
-; CHECK32-NEXT:    xor a0, a0, a1
-; CHECK32-NEXT:    ret
-;
-; CHECK64-LABEL: select_xor_2:
-; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    slli a2, a2, 63
-; CHECK64-NEXT:    srai a2, a2, 63
-; CHECK64-NEXT:    and a1, a2, a1
-; CHECK64-NEXT:    xor a0, a0, a1
-; CHECK64-NEXT:    ret
+; RV32IM-LABEL: select_xor_2:
+; RV32IM:       # %bb.0: # %entry
+; RV32IM-NEXT:    slli a2, a2, 31
+; RV32IM-NEXT:    srai a2, a2, 31
+; RV32IM-NEXT:    and a1, a2, a1
+; RV32IM-NEXT:    xor a0, a0, a1
+; RV32IM-NEXT:    ret
+;
+; RV64IM-LABEL: select_xor_2:
+; RV64IM:       # %bb.0: # %entry
+; RV64IM-NEXT:    slli a2, a2, 63
+; RV64IM-NEXT:    srai a2, a2, 63
+; RV64IM-NEXT:    and a1, a2, a1
+; RV64IM-NEXT:    xor a0, a0, a1
+; RV64IM-NEXT:    ret
+;
+; RV64IMXVTCONDOPS-LABEL: select_xor_2:
+; RV64IMXVTCONDOPS:       # %bb.0: # %entry
+; RV64IMXVTCONDOPS-NEXT:    andi a2, a2, 1
+; RV64IMXVTCONDOPS-NEXT:    vt.maskc a1, a1, a2
+; RV64IMXVTCONDOPS-NEXT:    xor a0, a0, a1
+; RV64IMXVTCONDOPS-NEXT:    ret
+;
+; CHECKZICOND-LABEL: select_xor_2:
+; CHECKZICOND:       # %bb.0: # %entry
+; CHECKZICOND-NEXT:    andi a2, a2, 1
+; CHECKZICOND-NEXT:    czero.eqz a1, a1, a2
+; CHECKZICOND-NEXT:    xor a0, a0, a1
+; CHECKZICOND-NEXT:    ret
 entry:
  %and = and i8 %cond, 1
  %cmp10 = icmp eq i8 %and, 0
@@ -296,21 +326,35 @@ entry:
 }
 
 define i32 @select_or(i32 %A, i32 %B, i8 %cond) {
-; CHECK32-LABEL: select_or:
-; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    slli a2, a2, 31
-; CHECK32-NEXT:    srai a2, a2, 31
-; CHECK32-NEXT:    and a1, a2, a1
-; CHECK32-NEXT:    or a0, a0, a1
-; CHECK32-NEXT:    ret
-;
-; CHECK64-LABEL: select_or:
-; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    slli a2, a2, 63
-; CHECK64-NEXT:    srai a2, a2, 63
-; CHECK64-NEXT:    and a1, a2, a1
-; CHECK64-NEXT:    or a0, a0, a1
-; CHECK64-NEXT:    ret
+; RV32IM-LABEL: select_or:
+; RV32IM:       # %bb.0: # %entry
+; RV32IM-NEXT:    slli a2, a2, 31
+; RV32IM-NEXT:    srai a2, a2, 31
+; RV32IM-NEXT:    and a1, a2, a1
+; RV32IM-NEXT:    or a0, a0, a1
+; RV32IM-NEXT:    ret
+;
+; RV64IM-LABEL: select_or:
+; RV64IM:       # %bb.0: # %entry
+; RV64IM-NEXT:    slli a2, a2, 63
+; RV64IM-NEXT:    srai a2, a2, 63
+; RV64IM-NEXT:    and a1, a2, a1
+; RV64IM-NEXT:    or a0, a0, a1
+; RV64IM-NEXT:    ret
+;
+; RV64IMXVTCONDOPS-LABEL: select_or:
+; RV64IMXVTCONDOPS:       # %bb.0: # %entry
+; RV64IMXVTCONDOPS-NEXT:    andi a2, a2, 1
+; RV64IMXVTCONDOPS-NEXT:    vt.maskc a1, a1, a2
+; RV64IMXVTCONDOPS-NEXT:    or a0, a0, a1
+; RV64IMXVTCONDOPS-NEXT:    ret
+;
+; CHECKZICOND-LABEL: select_or:
+; CHECKZICOND:       # %bb.0: # %entry
+; CHECKZICOND-NEXT:    andi a2, a2, 1
+; CHECKZICOND-NEXT:    czero.eqz a1, a1, a2
+; CHECKZICOND-NEXT:    or a0, a0, a1
+; CHECKZICOND-NEXT:    ret
 entry:
  %and = and i8 %cond, 1
  %cmp10 = icmp eq i8 %and, 0
@@ -360,21 +404,35 @@ entry:
 }
 
 define i32 @select_or_1(i32 %A, i32 %B, i32 %cond) {
-; CHECK32-LABEL: select_or_1:
-; CHECK32:       # %bb.0: # %entry
-; CHECK32-NEXT:    slli a2, a2, 31
-; CHECK32-NEXT:    srai a2, a2, 31
-; CHECK32-NEXT:    and a1, a2, a1
-; CHECK32-NEXT:    or a0, a0, a1
-; CHECK32-NEXT:    ret
-;
-; CHECK64-LABEL: select_or_1:
-; CHECK64:       # %bb.0: # %entry
-; CHECK64-NEXT:    slli a2, a2, 63
-; CHECK64-NEXT:    srai a2, a2, 63
-; CHECK64-NEXT:    and a1, a2, a1
-; CHECK64-NEXT:    or a0, a0, a1
-; CHECK64-NEXT:    ret
+; RV32IM-LABEL: select_or_1:
+; RV32IM:       # %bb.0: # %entry
+; RV32IM-NEXT:    slli a2, a2, 31
+; RV32IM-NEXT:    srai a2, a2, 31
+; RV32IM-NEXT:    and a1, a2, a1
+; RV32IM-NEXT:    or a0, a0, a1
+; RV32IM-NEXT:    ret
+;
+; RV64IM-LABEL: select_or_1:
+; RV64IM:       # %bb.0: # %entry
+; RV64IM-NEXT:    slli a2, a2, 63
+; RV64IM-NEXT:    srai a2, a2, 63
+; RV64IM-NEXT:    and a1, a2, a1
+; RV64IM-NEXT:    or a0, a0, a1
+; RV64IM-NEXT:    ret
+;
+; RV64IMXVTCONDOPS-LABEL: select_or_1:
+; RV64IMXVTCONDOPS:       # %bb.0: # %entry
+; RV64IMXVTCONDOPS-NEXT:    andi a2, a2, 1
+; RV64IMXVTCONDOPS-NEXT:    vt.maskc a1, a1, a2
+; RV64IMXVTCONDOPS-NEXT:    or a0, a0, a1
+; RV64IMXVTCONDOPS-NEXT:    ret
+;
+; CHECKZICOND-LABEL: select_or_1:
+; CHECKZICOND:       # %bb.0: # %entry
+; CHECKZICOND-NEXT:    andi a2, a2, 1
+; CHECKZICOND-NEXT:    czero.eqz a1, a1, a2
+; CHECKZICOND-NEXT:    or a0, a0, a1
+; CHECKZICOND-NEXT:    ret
 entry:
  %and = and i32 %cond, 1
  %cmp10 = icmp eq i32 %and, 0

``````````

</details>


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


More information about the llvm-commits mailing list