diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp index 5626db589381..36f7139ffc74 100644 --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp @@ -678,6 +678,125 @@ bool RISCVDAGToDAGISel::tryShrinkShlLogicImm(SDNode *Node) { return true; } +/// isInt32Immediate - This method tests to see if the node is a 32-bit constant +/// operand. If so Imm will receive the 32-bit value. +static bool isInt32Immediate(SDNode *N, unsigned &Imm) { + if (N->getOpcode() == ISD::Constant && N->getValueType(0) == MVT::i32) { + Imm = cast(N)->getZExtValue(); + return true; + } + return false; +} + +// isInt32Immediate - This method tests to see if a constant operand. +// If so Imm will receive the 32 bit value. +static bool isInt32Immediate(SDValue N, unsigned &Imm) { + return isInt32Immediate(N.getNode(), Imm); +} + +// isOpcWithIntImmediate - This method tests to see if the node is a specific +// opcode and that it has a immediate integer right operand. +// If so Imm will receive the 32 bit value. +static bool isOpcWithIntImmediate(SDNode *N, unsigned Opc, unsigned& Imm) { + return N->getOpcode() == Opc + && isInt32Immediate(N->getOperand(1).getNode(), Imm); +} + +bool RISCVDAGToDAGISel::tryXCVbitmanipExtractOp(SDNode *N, bool IsSigned) { + if (!Subtarget->hasExtXcvbitmanip()) + return false; + unsigned Opc = IsSigned ? RISCV::CV_EXTRACT : RISCV::CV_EXTRACTU; + SDLoc DL(N); + MVT XLenVT = Subtarget->getXLenVT(); + MVT VT = N->getSimpleValueType(0); + + // For unsigned extracts, check for a shift right and mask + unsigned AndImm = 0; + if (N->getOpcode() == ISD::AND) { + if (isOpcWithIntImmediate(N, ISD::AND, AndImm)) { + + // The immediate is a mask of the low bits iff imm & (imm+1) == 0 + if (AndImm & (AndImm + 1)) + return false; + + unsigned Srl_imm = 0; + if (isOpcWithIntImmediate(N->getOperand(0).getNode(), ISD::SRL, + Srl_imm)) { + assert(Srl_imm > 0 && Srl_imm < 32 && "bad amount in shift node!"); + + // Mask off the unnecessary bits of the AND immediate; normally + // DAGCombine will do this, but that might not happen if + // targetShrinkDemandedConstant chooses a different immediate. + AndImm &= -1U >> Srl_imm; + + unsigned Width = countTrailingOnes(AndImm); + unsigned LSB = Srl_imm; + + + if ((LSB + Width) == N->getValueType(0).getSizeInBits()) { + Opc = IsSigned ? RISCV::SRA : RISCV::SRL; + SDNode *NewNode = CurDAG->getMachineNode( + Opc, DL, VT, N->getOperand(0).getOperand(0)); + ReplaceNode(N, NewNode); + return true; + } + + assert(LSB + Width + 1 <= 32 && "cv.extract width will get shrank"); + SDNode *NewNode = CurDAG->getMachineNode( + Opc, DL, VT, N->getOperand(0).getOperand(0), + CurDAG->getTargetConstant(Width, DL, XLenVT), + CurDAG->getTargetConstant(LSB, DL, XLenVT)); + ReplaceNode(N, NewNode); + return true; + } + } + return false; + } + + // Otherwise, we're looking for a shift of a shift + unsigned Shl_imm = 0; + if (isOpcWithIntImmediate(N->getOperand(0).getNode(), ISD::SHL, Shl_imm)) { + assert(Shl_imm > 0 && Shl_imm < 32 && "bad amount in shift node!"); + unsigned Srl_imm = 0; + if (isInt32Immediate(N->getOperand(1), Srl_imm)) { + assert(Srl_imm > 0 && Srl_imm < 32 && "bad amount in shift node!"); + unsigned Width = 32 - Srl_imm; + int LSB = Srl_imm - Shl_imm; + if (LSB < 0) + return false; + assert(LSB + Width <= 32 && "cv.extract width will get shrank"); + SDNode *NewNode = CurDAG->getMachineNode( + Opc, DL, VT, N->getOperand(0).getOperand(0), + CurDAG->getTargetConstant(Width, DL, XLenVT), + CurDAG->getTargetConstant(LSB, DL, XLenVT)); + ReplaceNode(N, NewNode); + return true; + } + } + + // Or we are looking for a shift of an and, with a mask operand + if (isOpcWithIntImmediate(N->getOperand(0).getNode(), ISD::AND, AndImm) && + isShiftedMask_32(AndImm)) { + unsigned Srl_imm = 0; + unsigned LSB = countTrailingZeros(AndImm); + // Shift must be the same as the ands lsb + if (isInt32Immediate(N->getOperand(1), Srl_imm) && Srl_imm == LSB) { + assert(Srl_imm > 0 && Srl_imm < 32 && "bad amount in shift node!"); + unsigned MSB = 31 - countLeadingZeros(AndImm); + unsigned Width = MSB - LSB; + assert(Srl_imm + Width <= 32 && "cv.extract width will get shrank"); + SDNode *NewNode = CurDAG->getMachineNode( + Opc, DL, VT, N->getOperand(0).getOperand(0), + CurDAG->getTargetConstant(Width, DL, XLenVT), + CurDAG->getTargetConstant(LSB, DL, XLenVT)); + ReplaceNode(N, NewNode); + return true; + } + } + + return false; +} + void RISCVDAGToDAGISel::Select(SDNode *Node) { // If we have a custom node, we have already selected. if (Node->isMachineOpcode()) { @@ -717,6 +836,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { return; } case ISD::SHL: { + if (tryXCVbitmanipExtractOp(Node, false)) + return; auto *N1C = dyn_cast(Node->getOperand(1)); if (!N1C) break; @@ -747,6 +868,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { break; } case ISD::SRL: { + if (tryXCVbitmanipExtractOp(Node, false)) + return; auto *N1C = dyn_cast(Node->getOperand(1)); if (!N1C) break; @@ -819,6 +942,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { return; } case ISD::SRA: { + if (tryXCVbitmanipExtractOp(Node, true)) + return; // Optimize (sra (sext_inreg X, i16), C) -> // (srai (slli X, (XLen-16), (XLen-16) + C) // And (sra (sext_inreg X, i8), C) -> @@ -856,6 +981,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) { break; case ISD::AND: { + if (tryXCVbitmanipExtractOp(Node, false)) + return; auto *N1C = dyn_cast(Node->getOperand(1)); if (!N1C) break; diff --git a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h index 15e1a1ef36cf..24ab2470488a 100644 --- a/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h +++ b/llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h @@ -53,6 +53,8 @@ class RISCVDAGToDAGISel : public SelectionDAGISel { bool tryShrinkShlLogicImm(SDNode *Node); + bool tryXCVbitmanipExtractOp(SDNode *N, bool isSigned); + bool selectShiftMask(SDValue N, unsigned ShiftWidth, SDValue &ShAmt); bool selectShiftMaskXLen(SDValue N, SDValue &ShAmt) { return selectShiftMask(N, Subtarget->getXLen(), ShAmt); diff --git a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp index c2a6536ed7f5..79390ee0f5b2 100644 --- a/llvm/lib/Target/RISCV/RISCVISelLowering.cpp +++ b/llvm/lib/Target/RISCV/RISCVISelLowering.cpp @@ -433,7 +433,7 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM, } if (Subtarget.hasExtXcvbitmanip()) { - setOperationAction({ISD::CTPOP}, XLenVT, Legal); + setOperationAction(ISD::CTPOP, XLenVT, Legal); } if (Subtarget.hasExtXcvmem()) { diff --git a/llvm/test/CodeGen/RISCV/corev/bitmanip.ll b/llvm/test/CodeGen/RISCV/corev/bitmanip.ll index 90110f11d17b..9203a67f4c2d 100644 --- a/llvm/test/CodeGen/RISCV/corev/bitmanip.ll +++ b/llvm/test/CodeGen/RISCV/corev/bitmanip.ll @@ -187,3 +187,134 @@ define i32 @test.cv.bitrev(i32 %a) { %1 = call i32 @llvm.riscv.cv.bitmanip.bitrev(i32 %a, i32 1, i32 2) ret i32 %1 } + +define i32 @sbfx1(i32 %a) { +; CHECK-LABEL: sbfx1: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extract a0, a0, 11, 7 +; CHECK-NEXT: ret + %t1 = lshr i32 %a, 7 + %t2 = trunc i32 %t1 to i11 + %t3 = sext i11 %t2 to i32 + ret i32 %t3 +} + +define i32 @ubfx1(i32 %a) { +; CHECK-LABEL: ubfx1: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extractu a0, a0, 11, 7 +; CHECK-NEXT: ret + %t1 = lshr i32 %a, 7 + %t2 = trunc i32 %t1 to i11 + %t3 = zext i11 %t2 to i32 + ret i32 %t3 +} + +define i32 @ubfx2(i32 %a) { +; CHECK-LABEL: ubfx2: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extractu a0, a0, 11, 7 +; CHECK-NEXT: ret + %t1 = lshr i32 %a, 7 + %t2 = and i32 %t1, 2047 + ret i32 %t2 +} + +define i32 @ubfx3(i32 %a) { +; CHECK-LABEL: ubfx3: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extractu a0, a0, 0, 11 +; CHECK-NEXT: ret + %t1 = and i32 %a, 2048 + %t2 = lshr i32 %t1, 11 + ret i32 %t2 +} + +define i32 @ubfx4(i32 %a) { +; CHECK-LABEL: ubfx4: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extractu a0, a0, 2, 7 +; CHECK-NEXT: ret + %t1 = and i32 %a, 896 + %t2 = lshr i32 %t1, 7 + ret i32 %t2 +} + +define i32 @f1(i32 %a) { +; CHECK-LABEL: f1: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cv.extract a0, a0, 20, 0 +; CHECK-NEXT: ret +entry: + %tmp = shl i32 %a, 12 + %tmp2 = ashr i32 %tmp, 12 + ret i32 %tmp2 +} + +define i32 @f2(i32 %a) { +; CHECK-LABEL: f2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: slli a0, a0, 12 +; CHECK-NEXT: srli a0, a0, 12 +; CHECK-NEXT: ret +entry: + %tmp = shl i32 %a, 12 + %tmp2 = lshr i32 %tmp, 12 + ret i32 %tmp2 +} + +define i32 @f3(i32 %a) { +; CHECK-LABEL: f3: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cv.extract a0, a0, 3, 5 +; CHECK-NEXT: ret +entry: + %tmp = shl i32 %a, 24 + %tmp2 = ashr i32 %tmp, 29 + ret i32 %tmp2 +} + +define i32 @f4(i32 %a) { +; CHECK-LABEL: f4: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cv.extractu a0, a0, 3, 5 +; CHECK-NEXT: ret +entry: + %tmp = shl i32 %a, 24 + %tmp2 = lshr i32 %tmp, 29 + ret i32 %tmp2 +} + +define i32 @f5(i32 %a) { +; CHECK-LABEL: f5: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: slli a0, a0, 3 +; CHECK-NEXT: srai a0, a0, 1 +; CHECK-NEXT: ret +entry: + %tmp = shl i32 %a, 3 + %tmp2 = ashr i32 %tmp, 1 + ret i32 %tmp2 +} + +define signext i8 @f6(i32 %a) { +; CHECK-LABEL: f6: +; CHECK: # %bb.0: +; CHECK-NEXT: cv.extract a0, a0, 8, 23 +; CHECK-NEXT: ret + + %tmp = lshr i32 %a, 23 + %res = trunc i32 %tmp to i8 + ret i8 %res +} + +define signext i8 @f7(i32 %a) { +; CHECK-LABEL: f7: +; CHECK: # %bb.0: +; CHECK-NEXT: srli a0, a0, 25 +; CHECK-NEXT: ret + + %tmp = lshr i32 %a, 25 + %res = trunc i32 %tmp to i8 + ret i8 %res +}