Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RISCV] Codegen Support for bitmanip #105

Draft
wants to merge 3 commits into
base: development
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<ConstantSDNode>(N)->getZExtValue();
return true;
}
return false;
}

// isInt32Immediate - This method tests to see if a constant operand.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Incomplete comment?

// 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;

// Note: The width operand is encoded as width-1.
unsigned Width = countTrailingOnes(AndImm) - 1;
unsigned LSB = Srl_imm;

if ((LSB + Width + 1) == 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 - 1;
int LSB = Srl_imm - Shl_imm;
if (LSB < 0)
return false;
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;
}
}

// 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 + 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(Srl_imm, 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()) {
Expand Down Expand Up @@ -717,6 +836,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
return;
}
case ISD::SHL: {
if (tryXCVbitmanipExtractOp(Node, false))
return;
auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
if (!N1C)
break;
Expand Down Expand Up @@ -747,6 +868,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
break;
}
case ISD::SRL: {
if (tryXCVbitmanipExtractOp(Node, false))
return;
auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
if (!N1C)
break;
Expand Down Expand Up @@ -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) ->
Expand Down Expand Up @@ -856,6 +981,8 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {

break;
case ISD::AND: {
if (tryXCVbitmanipExtractOp(Node, false))
return;
auto *N1C = dyn_cast<ConstantSDNode>(Node->getOperand(1));
if (!N1C)
break;
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelDAGToDAG.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,10 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Legal);
}

if (Subtarget.hasExtXcvbitmanip()) {
setOperationAction(ISD::CTPOP, XLenVT, Legal);
}

if (Subtarget.hasExtXcvmem()) {
setIndexedLoadAction(ISD::POST_INC, MVT::i8, Legal);
setIndexedLoadAction(ISD::POST_INC, MVT::i16, Legal);
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfoCOREV.td
Original file line number Diff line number Diff line change
Expand Up @@ -1226,6 +1226,7 @@ let Predicates = [HasExtXcvbitmanip, IsRV32] in {

def : Pat<(int_riscv_cv_bitmanip_bitrev GPR:$rs1, cv_tuimm5:$pts, cv_tuimm2:$radix),
(CV_BITREV GPR:$rs1, cv_tuimm2:$radix, cv_tuimm5:$pts)>;
def : PatGpr<ctpop, CV_CNT>;
}

let Predicates = [HasExtXcvelw, IsRV32] in {
Expand Down
142 changes: 142 additions & 0 deletions llvm/test/CodeGen/RISCV/corev/bitmanip.ll
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,17 @@ define i32 @test.cv.cnt(i32 %a) {
ret i32 %1
}

declare i32 @llvm.ctpop.i32(i32)

define i32 @ctpop_i32(i32 %a) {
; CHECK-LABEL: ctpop_i32:
; CHECK: # %bb.0:
; CHECK-NEXT: cv.cnt a0, a0
; CHECK-NEXT: ret
%1 = call i32 @llvm.ctpop.i32(i32 %a)
ret i32 %1
}

declare i32 @llvm.fshr.i32(i32, i32, i32)

define i32 @test.cv.ror(i32 %a, i32 %b) {
Expand All @@ -176,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, 10, 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, 10, 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, 10, 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, 19, 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, 2, 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, 2, 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, 7, 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
}
Loading