diff --git a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp --- a/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp +++ b/llvm/lib/Target/LoongArch/LoongArchISelLowering.cpp @@ -75,6 +75,7 @@ setMinFunctionAlignment(FunctionAlignment); setTargetDAGCombine(ISD::AND); + setTargetDAGCombine(ISD::SRL); } SDValue LoongArchTargetLowering::LowerOperation(SDValue Op, @@ -303,6 +304,47 @@ DAG.getConstant(lsb, DL, GRLenVT)); } +static SDValue performSRLCombine(SDNode *N, SelectionDAG &DAG, + TargetLowering::DAGCombinerInfo &DCI, + const LoongArchSubtarget &Subtarget) { + if (DCI.isBeforeLegalizeOps()) + return SDValue(); + + // $dst = srl (and $src, Mask), Shamt + // => + // BSTRPICK $dst, $src, MaskIdx+MaskLen-1, Shamt + // when Mask is a shifted mask, and MaskIdx <= Shamt <= MaskIdx+MaskLen-1 + // + + SDValue FirstOperand = N->getOperand(0); + ConstantSDNode *CN; + EVT ValTy = N->getValueType(0); + SDLoc DL(N); + MVT GRLenVT = Subtarget.getGRLenVT(); + unsigned MaskIdx, MaskLen; + uint64_t Shamt; + + // The first operand must be an AND and the second operand of the AND must be + // a shifted mask. + if (FirstOperand.getOpcode() != ISD::AND || + !(CN = dyn_cast(FirstOperand.getOperand(1))) || + !isShiftedMask_64(CN->getZExtValue(), MaskIdx, MaskLen)) + return SDValue(); + + // The second operand (shift amount) must be an immediate. + if (!(CN = dyn_cast(N->getOperand(1)))) + return SDValue(); + + Shamt = CN->getZExtValue(); + if (MaskIdx <= Shamt && Shamt <= MaskIdx + MaskLen - 1) + return DAG.getNode(LoongArchISD::BSTRPICK, DL, ValTy, + FirstOperand->getOperand(0), + DAG.getConstant(MaskIdx + MaskLen - 1, DL, GRLenVT), + DAG.getConstant(Shamt, DL, GRLenVT)); + + return SDValue(); +} + SDValue LoongArchTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { SelectionDAG &DAG = DCI.DAG; @@ -311,6 +353,8 @@ break; case ISD::AND: return performANDCombine(N, DAG, DCI, Subtarget); + case ISD::SRL: + return performSRLCombine(N, DAG, DCI, Subtarget); } return SDValue(); } diff --git a/llvm/test/CodeGen/LoongArch/bstrpick_d.ll b/llvm/test/CodeGen/LoongArch/bstrpick_d.ll --- a/llvm/test/CodeGen/LoongArch/bstrpick_d.ll +++ b/llvm/test/CodeGen/LoongArch/bstrpick_d.ll @@ -47,3 +47,51 @@ %and = and i64 %a, 4095 ret i64 %and } + +;; (srl (and a, 0xff0), 4) => (BSTRPICK a, 11, 4) +define i64 @and0xff0_lshr4(i64 %a) { +; CHECK-LABEL: and0xff0_lshr4: +; CHECK: # %bb.0: +; CHECK-NEXT: bstrpick.d $a0, $a0, 11, 4 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i64 %a, 4080 + %shr = lshr i64 %and, 4 + ret i64 %shr +} + +;; (sra (and a, 0xff0), 5) can also be combined to (BSTRPICK a, 11, 5). +;; This is because (sra (and a, 0xff0)) would be combined to (srl (and a, 0xff0), 5) +;; firstly by DAGCombiner::SimplifyDemandedBits. +define i64 @and4080_ashr5(i64 %a) { +; CHECK-LABEL: and4080_ashr5: +; CHECK: # %bb.0: +; CHECK-NEXT: bstrpick.d $a0, $a0, 11, 5 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i64 %a, 4080 + %shr = ashr i64 %and, 5 + ret i64 %shr +} + +;; Negative test: the second operand of AND is not a shifted mask +define i64 @and0xf30_lshr4(i64 %a) { +; CHECK-LABEL: and0xf30_lshr4: +; CHECK: # %bb.0: +; CHECK-NEXT: andi $a0, $a0, 3888 +; CHECK-NEXT: srli.d $a0, $a0, 4 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i64 %a, 3888 + %shr = lshr i64 %and, 4 + ret i64 %shr +} + +;; Negative test: Shamt < MaskIdx +define i64 @and0xff0_lshr3(i64 %a) { +; CHECK-LABEL: and0xff0_lshr3: +; CHECK: # %bb.0: +; CHECK-NEXT: andi $a0, $a0, 4080 +; CHECK-NEXT: srli.d $a0, $a0, 3 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i64 %a, 4080 + %shr = lshr i64 %and, 3 + ret i64 %shr +} diff --git a/llvm/test/CodeGen/LoongArch/bstrpick_w.ll b/llvm/test/CodeGen/LoongArch/bstrpick_w.ll --- a/llvm/test/CodeGen/LoongArch/bstrpick_w.ll +++ b/llvm/test/CodeGen/LoongArch/bstrpick_w.ll @@ -47,3 +47,51 @@ %and = and i32 %a, 4095 ret i32 %and } + +;; (srl (and a, 0xff0), 4) => (BSTRPICK a, 11, 4) +define i32 @and0xff0_lshr4(i32 %a) { +; CHECK-LABEL: and0xff0_lshr4: +; CHECK: # %bb.0: +; CHECK-NEXT: bstrpick.w $a0, $a0, 11, 4 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i32 %a, 4080 + %shr = lshr i32 %and, 4 + ret i32 %shr +} + +;; (sra (and a, 0xff0), 5) can also be combined to (BSTRPICK a, 11, 5). +;; This is because (sra (and a, 0xff0)) would be combined to (srl (and a, 0xff0), 5) +;; firstly by DAGCombiner::SimplifyDemandedBits. +define i32 @and4080_ashr5(i32 %a) { +; CHECK-LABEL: and4080_ashr5: +; CHECK: # %bb.0: +; CHECK-NEXT: bstrpick.w $a0, $a0, 11, 5 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i32 %a, 4080 + %shr = ashr i32 %and, 5 + ret i32 %shr +} + +;; Negative test: the second operand of AND is not a shifted mask +define i32 @and0xf30_lshr4(i32 %a) { +; CHECK-LABEL: and0xf30_lshr4: +; CHECK: # %bb.0: +; CHECK-NEXT: andi $a0, $a0, 3888 +; CHECK-NEXT: srli.w $a0, $a0, 4 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i32 %a, 3888 + %shr = lshr i32 %and, 4 + ret i32 %shr +} + +;; Negative test: Shamt < MaskIdx +define i32 @and0xff0_lshr3(i32 %a) { +; CHECK-LABEL: and0xff0_lshr3: +; CHECK: # %bb.0: +; CHECK-NEXT: andi $a0, $a0, 4080 +; CHECK-NEXT: srli.w $a0, $a0, 3 +; CHECK-NEXT: jirl $zero, $ra, 0 + %and = and i32 %a, 4080 + %shr = lshr i32 %and, 3 + ret i32 %shr +} diff --git a/llvm/test/CodeGen/LoongArch/ir-instruction/lshr.ll b/llvm/test/CodeGen/LoongArch/ir-instruction/lshr.ll --- a/llvm/test/CodeGen/LoongArch/ir-instruction/lshr.ll +++ b/llvm/test/CodeGen/LoongArch/ir-instruction/lshr.ll @@ -103,14 +103,12 @@ define i8 @lshr_i8_3(i8 %x) { ; LA32-LABEL: lshr_i8_3: ; LA32: # %bb.0: -; LA32-NEXT: andi $a0, $a0, 248 -; LA32-NEXT: srli.w $a0, $a0, 3 +; LA32-NEXT: bstrpick.w $a0, $a0, 7, 3 ; LA32-NEXT: jirl $zero, $ra, 0 ; ; LA64-LABEL: lshr_i8_3: ; LA64: # %bb.0: -; LA64-NEXT: andi $a0, $a0, 248 -; LA64-NEXT: srli.d $a0, $a0, 3 +; LA64-NEXT: bstrpick.d $a0, $a0, 7, 3 ; LA64-NEXT: jirl $zero, $ra, 0 %lshr = lshr i8 %x, 3 ret i8 %lshr @@ -119,18 +117,12 @@ define i16 @lshr_i16_3(i16 %x) { ; LA32-LABEL: lshr_i16_3: ; LA32: # %bb.0: -; LA32-NEXT: lu12i.w $a1, 15 -; LA32-NEXT: ori $a1, $a1, 4088 -; LA32-NEXT: and $a0, $a0, $a1 -; LA32-NEXT: srli.w $a0, $a0, 3 +; LA32-NEXT: bstrpick.w $a0, $a0, 15, 3 ; LA32-NEXT: jirl $zero, $ra, 0 ; ; LA64-LABEL: lshr_i16_3: ; LA64: # %bb.0: -; LA64-NEXT: lu12i.w $a1, 15 -; LA64-NEXT: ori $a1, $a1, 4088 -; LA64-NEXT: and $a0, $a0, $a1 -; LA64-NEXT: srli.d $a0, $a0, 3 +; LA64-NEXT: bstrpick.d $a0, $a0, 15, 3 ; LA64-NEXT: jirl $zero, $ra, 0 %lshr = lshr i16 %x, 3 ret i16 %lshr @@ -144,10 +136,7 @@ ; ; LA64-LABEL: lshr_i32_3: ; LA64: # %bb.0: -; LA64-NEXT: addi.w $a1, $zero, -8 -; LA64-NEXT: lu32i.d $a1, 0 -; LA64-NEXT: and $a0, $a0, $a1 -; LA64-NEXT: srli.d $a0, $a0, 3 +; LA64-NEXT: bstrpick.d $a0, $a0, 31, 3 ; LA64-NEXT: jirl $zero, $ra, 0 %lshr = lshr i32 %x, 3 ret i32 %lshr