diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index ac5c0af1d34e0..3a6d388e5734e 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 5255d5c2792b3..11a30c834fc23 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 &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 b2a1ba0508ea3..163a3076bb449 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@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()