[llvm] 0ebb02b - [RISCV] Override TargetLowering::shouldProduceAndByConstByHoistingConstFromShiftsLHSOfAnd.

Craig Topper via llvm-commits llvm-commits at lists.llvm.org
Wed May 11 11:13:39 PDT 2022


Author: Craig Topper
Date: 2022-05-11T11:13:17-07:00
New Revision: 0ebb02b90a47b43e039cb7040cc9fb7dd5ec5fce

URL: https://github.com/llvm/llvm-project/commit/0ebb02b90a47b43e039cb7040cc9fb7dd5ec5fce
DIFF: https://github.com/llvm/llvm-project/commit/0ebb02b90a47b43e039cb7040cc9fb7dd5ec5fce.diff

LOG: [RISCV] Override TargetLowering::shouldProduceAndByConstByHoistingConstFromShiftsLHSOfAnd.

This hook determines if SimplifySetcc transforms (X & (C l>>/<< Y))
==/!= 0 into ((X <</l>> Y) & C) ==/!= 0. Where C is a constant and
X might be a constant.

The default implementation favors doing the transform if X is not
a constant. Otherwise the code is left alone. There is a provision
that if the target supports a bit test instruction then the transform
will favor ((1 << Y) & X) ==/!= 0. RISCV does not say it has a variable
bit test operation.

RISCV with Zbs does have a BEXT instruction that performs (X >> Y) & 1.
Without Zbs, (X >> Y) & 1 still looks preferable to ((1 << Y) & X) since
we can fold use ANDI instead of putting a 1 in a register for SLL.

This patch overrides this hook to favor bit extract patterns and
otherwise falls back to the "do the transform if X is not a constant"
heuristic.

I've added tests where both C and X are constants with both the shl form
and lshr form. I've also added a test for a switch statement that lowers
to a bit test. That was my original motivation for looking at this.

Reviewed By: asb

Differential Revision: https://reviews.llvm.org/D124639

Added: 
    

Modified: 
    llvm/lib/Target/RISCV/RISCVISelLowering.cpp
    llvm/lib/Target/RISCV/RISCVISelLowering.h
    llvm/test/CodeGen/RISCV/bittest.ll

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
index ac5c0af1d34e..3a6d388e5734 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp
@@ -1114,6 +1114,30 @@ bool RISCVTargetLowering::hasBitTest(SDValue X, SDValue Y) const {
   return C && C->getAPIntValue().ule(10);
 }
 
+bool RISCVTargetLowering::
+    shouldProduceAndByConstByHoistingConstFromShiftsLHSOfAnd(
+        SDValue X, ConstantSDNode *XC, ConstantSDNode *CC, SDValue Y,
+        unsigned OldShiftOpcode, unsigned NewShiftOpcode,
+        SelectionDAG &DAG) const {
+  // One interesting pattern that we'd want to form is 'bit extract':
+  //   ((1 >> Y) & 1) ==/!= 0
+  // But we also need to be careful not to try to reverse that fold.
+
+  // Is this '((1 >> Y) & 1)'?
+  if (XC && OldShiftOpcode == ISD::SRL && XC->isOne())
+    return false; // Keep the 'bit extract' pattern.
+
+  // Will this be '((1 >> Y) & 1)' after the transform?
+  if (NewShiftOpcode == ISD::SRL && CC->isOne())
+    return true; // Do form the 'bit extract' pattern.
+
+  // If 'X' is a constant, and we transform, then we will immediately
+  // try to undo the fold, thus causing endless combine loop.
+  // So only do the transform if X is not a constant. This matches the default
+  // implementation of this function.
+  return !XC;
+}
+
 /// Check if sinking \p I's operands to I's basic block is profitable, because
 /// the operands can be folded into a target instruction, e.g.
 /// splats of scalars can fold into vector instructions.

diff  --git a/llvm/lib/Target/RISCV/RISCVISelLowering.h b/llvm/lib/Target/RISCV/RISCVISelLowering.h
index 5255d5c2792b..11a30c834fc2 100644
--- a/llvm/lib/Target/RISCV/RISCVISelLowering.h
+++ b/llvm/lib/Target/RISCV/RISCVISelLowering.h
@@ -348,6 +348,10 @@ class RISCVTargetLowering : public TargetLowering {
   bool isCheapToSpeculateCtlz() const override;
   bool hasAndNotCompare(SDValue Y) const override;
   bool hasBitTest(SDValue X, SDValue Y) const override;
+  bool shouldProduceAndByConstByHoistingConstFromShiftsLHSOfAnd(
+      SDValue X, ConstantSDNode *XC, ConstantSDNode *CC, SDValue Y,
+      unsigned OldShiftOpcode, unsigned NewShiftOpcode,
+      SelectionDAG &DAG) const override;
   bool shouldSinkOperands(Instruction *I,
                           SmallVectorImpl<Use *> &Ops) const override;
   bool isFPImmLegal(const APFloat &Imm, EVT VT,

diff  --git a/llvm/test/CodeGen/RISCV/bittest.ll b/llvm/test/CodeGen/RISCV/bittest.ll
index b2a1ba0508ea..163a3076bb44 100644
--- a/llvm/test/CodeGen/RISCV/bittest.ll
+++ b/llvm/test/CodeGen/RISCV/bittest.ll
@@ -211,3 +211,228 @@ define i64 @bittest_63_i64(i64 %a) nounwind {
   %and = and i64 %not, 1
   ret i64 %and
 }
+
+; Make sure we use (andi (srl X, Y), 1) or bext.
+define i1 @bittest_constant_by_var_shr_i32(i32 signext %b) nounwind {
+; RV32I-LABEL: bittest_constant_by_var_shr_i32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 301408
+; RV32I-NEXT:    addi a1, a1, 722
+; RV32I-NEXT:    srl a0, a1, a0
+; RV32I-NEXT:    andi a0, a0, 1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: bittest_constant_by_var_shr_i32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a1, 301408
+; RV64I-NEXT:    addiw a1, a1, 722
+; RV64I-NEXT:    srlw a0, a1, a0
+; RV64I-NEXT:    andi a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV32ZBS-LABEL: bittest_constant_by_var_shr_i32:
+; RV32ZBS:       # %bb.0:
+; RV32ZBS-NEXT:    lui a1, 301408
+; RV32ZBS-NEXT:    addi a1, a1, 722
+; RV32ZBS-NEXT:    bext a0, a1, a0
+; RV32ZBS-NEXT:    ret
+;
+; RV64ZBS-LABEL: bittest_constant_by_var_shr_i32:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    lui a1, 301408
+; RV64ZBS-NEXT:    addiw a1, a1, 722
+; RV64ZBS-NEXT:    bext a0, a1, a0
+; RV64ZBS-NEXT:    ret
+  %shl = lshr i32 1234567890, %b
+  %and = and i32 %shl, 1
+  %cmp = icmp ne i32 %and, 0
+  ret i1 %cmp
+}
+
+; Make sure we use (andi (srl X, Y), 1) or bext.
+define i1 @bittest_constant_by_var_shl_i32(i32 signext %b) nounwind {
+; RV32I-LABEL: bittest_constant_by_var_shl_i32:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    lui a1, 301408
+; RV32I-NEXT:    addi a1, a1, 722
+; RV32I-NEXT:    srl a0, a1, a0
+; RV32I-NEXT:    andi a0, a0, 1
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: bittest_constant_by_var_shl_i32:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a1, 301408
+; RV64I-NEXT:    addiw a1, a1, 722
+; RV64I-NEXT:    srlw a0, a1, a0
+; RV64I-NEXT:    andi a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV32ZBS-LABEL: bittest_constant_by_var_shl_i32:
+; RV32ZBS:       # %bb.0:
+; RV32ZBS-NEXT:    lui a1, 301408
+; RV32ZBS-NEXT:    addi a1, a1, 722
+; RV32ZBS-NEXT:    bext a0, a1, a0
+; RV32ZBS-NEXT:    ret
+;
+; RV64ZBS-LABEL: bittest_constant_by_var_shl_i32:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    lui a1, 301408
+; RV64ZBS-NEXT:    addiw a1, a1, 722
+; RV64ZBS-NEXT:    bext a0, a1, a0
+; RV64ZBS-NEXT:    ret
+  %shl = shl i32 1, %b
+  %and = and i32 %shl, 1234567890
+  %cmp = icmp ne i32 %and, 0
+  ret i1 %cmp
+}
+
+; Make sure we use (andi (srl X, Y), 1) or bext.
+define i1 @bittest_constant_by_var_shr_i64(i64 %b) nounwind {
+; RV32-LABEL: bittest_constant_by_var_shr_i64:
+; RV32:       # %bb.0:
+; RV32-NEXT:    addi a1, a0, -32
+; RV32-NEXT:    bltz a1, .LBB12_2
+; RV32-NEXT:  # %bb.1:
+; RV32-NEXT:    andi a0, zero, 1
+; RV32-NEXT:    ret
+; RV32-NEXT:  .LBB12_2:
+; RV32-NEXT:    lui a1, 301408
+; RV32-NEXT:    addi a1, a1, 722
+; RV32-NEXT:    srl a0, a1, a0
+; RV32-NEXT:    andi a0, a0, 1
+; RV32-NEXT:    ret
+;
+; RV64I-LABEL: bittest_constant_by_var_shr_i64:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a1, 301408
+; RV64I-NEXT:    addiw a1, a1, 722
+; RV64I-NEXT:    srl a0, a1, a0
+; RV64I-NEXT:    andi a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bittest_constant_by_var_shr_i64:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    lui a1, 301408
+; RV64ZBS-NEXT:    addiw a1, a1, 722
+; RV64ZBS-NEXT:    bext a0, a1, a0
+; RV64ZBS-NEXT:    ret
+  %shl = lshr i64 1234567890, %b
+  %and = and i64 %shl, 1
+  %cmp = icmp ne i64 %and, 0
+  ret i1 %cmp
+}
+
+; Make sure we use (andi (srl X, Y), 1) or bext.
+define i1 @bittest_constant_by_var_shl_i64(i64 %b) nounwind {
+; RV32-LABEL: bittest_constant_by_var_shl_i64:
+; RV32:       # %bb.0:
+; RV32-NEXT:    addi a1, a0, -32
+; RV32-NEXT:    bltz a1, .LBB13_2
+; RV32-NEXT:  # %bb.1:
+; RV32-NEXT:    andi a0, zero, 1
+; RV32-NEXT:    ret
+; RV32-NEXT:  .LBB13_2:
+; RV32-NEXT:    lui a1, 301408
+; RV32-NEXT:    addi a1, a1, 722
+; RV32-NEXT:    srl a0, a1, a0
+; RV32-NEXT:    andi a0, a0, 1
+; RV32-NEXT:    ret
+;
+; RV64I-LABEL: bittest_constant_by_var_shl_i64:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    lui a1, 301408
+; RV64I-NEXT:    addiw a1, a1, 722
+; RV64I-NEXT:    srl a0, a1, a0
+; RV64I-NEXT:    andi a0, a0, 1
+; RV64I-NEXT:    ret
+;
+; RV64ZBS-LABEL: bittest_constant_by_var_shl_i64:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    lui a1, 301408
+; RV64ZBS-NEXT:    addiw a1, a1, 722
+; RV64ZBS-NEXT:    bext a0, a1, a0
+; RV64ZBS-NEXT:    ret
+  %shl = shl i64 1, %b
+  %and = and i64 %shl, 1234567890
+  %cmp = icmp ne i64 %and, 0
+  ret i1 %cmp
+}
+
+; We want to use (andi (srl X, Y), 1) or bext before the beqz.
+define void @bittest_switch(i32 signext %0) {
+; RV32I-LABEL: bittest_switch:
+; RV32I:       # %bb.0:
+; RV32I-NEXT:    li a1, 31
+; RV32I-NEXT:    bltu a1, a0, .LBB14_3
+; RV32I-NEXT:  # %bb.1:
+; RV32I-NEXT:    lui a1, 524291
+; RV32I-NEXT:    addi a1, a1, 768
+; RV32I-NEXT:    srl a0, a1, a0
+; RV32I-NEXT:    andi a0, a0, 1
+; RV32I-NEXT:    beqz a0, .LBB14_3
+; RV32I-NEXT:  # %bb.2:
+; RV32I-NEXT:    tail bar at plt
+; RV32I-NEXT:  .LBB14_3:
+; RV32I-NEXT:    ret
+;
+; RV64I-LABEL: bittest_switch:
+; RV64I:       # %bb.0:
+; RV64I-NEXT:    li a1, 31
+; RV64I-NEXT:    bltu a1, a0, .LBB14_3
+; RV64I-NEXT:  # %bb.1:
+; RV64I-NEXT:    lui a1, 2048
+; RV64I-NEXT:    addiw a1, a1, 51
+; RV64I-NEXT:    slli a1, a1, 8
+; RV64I-NEXT:    srl a0, a1, a0
+; RV64I-NEXT:    andi a0, a0, 1
+; RV64I-NEXT:    beqz a0, .LBB14_3
+; RV64I-NEXT:  # %bb.2:
+; RV64I-NEXT:    tail bar at plt
+; RV64I-NEXT:  .LBB14_3:
+; RV64I-NEXT:    ret
+;
+; RV32ZBS-LABEL: bittest_switch:
+; RV32ZBS:       # %bb.0:
+; RV32ZBS-NEXT:    li a1, 31
+; RV32ZBS-NEXT:    bltu a1, a0, .LBB14_3
+; RV32ZBS-NEXT:  # %bb.1:
+; RV32ZBS-NEXT:    lui a1, 524291
+; RV32ZBS-NEXT:    addi a1, a1, 768
+; RV32ZBS-NEXT:    bext a0, a1, a0
+; RV32ZBS-NEXT:    beqz a0, .LBB14_3
+; RV32ZBS-NEXT:  # %bb.2:
+; RV32ZBS-NEXT:    tail bar at plt
+; RV32ZBS-NEXT:  .LBB14_3:
+; RV32ZBS-NEXT:    ret
+;
+; RV64ZBS-LABEL: bittest_switch:
+; RV64ZBS:       # %bb.0:
+; RV64ZBS-NEXT:    li a1, 31
+; RV64ZBS-NEXT:    bltu a1, a0, .LBB14_3
+; RV64ZBS-NEXT:  # %bb.1:
+; RV64ZBS-NEXT:    lui a1, 2048
+; RV64ZBS-NEXT:    addiw a1, a1, 51
+; RV64ZBS-NEXT:    slli a1, a1, 8
+; RV64ZBS-NEXT:    bext a0, a1, a0
+; RV64ZBS-NEXT:    beqz a0, .LBB14_3
+; RV64ZBS-NEXT:  # %bb.2:
+; RV64ZBS-NEXT:    tail bar at plt
+; RV64ZBS-NEXT:  .LBB14_3:
+; RV64ZBS-NEXT:    ret
+  switch i32 %0, label %3 [
+    i32 8, label %2
+    i32 9, label %2
+    i32 12, label %2
+    i32 13, label %2
+    i32 31, label %2
+  ]
+
+2:
+  tail call void @bar()
+  br label %3
+
+3:
+  ret void
+}
+
+declare void @bar()


        


More information about the llvm-commits mailing list