[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
This commit is contained in:
Craig Topper 2022-05-11 10:58:10 -07:00
parent 99ef341ce9
commit 0ebb02b90a
3 changed files with 253 additions and 0 deletions

View File

@ -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.

View File

@ -348,6 +348,10 @@ public:
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,

View File

@ -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@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@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@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@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()