Skip to content

Commit

Permalink
add CodeGen pattern for cv.extract[u] from shift+mask and shift+shift…
Browse files Browse the repository at this point in the history
… operation
  • Loading branch information
realqhc committed Jun 3, 2024
1 parent ed4d683 commit 2ed435d
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 1 deletion.
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.
// 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()) {
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
2 changes: 1 addition & 1 deletion llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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()) {
Expand Down
131 changes: 131 additions & 0 deletions llvm/test/CodeGen/RISCV/corev/bitmanip.ll
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

0 comments on commit 2ed435d

Please sign in to comment.