Skip to content

Commit

Permalink
[RISCV] Introduce codegen patterns for instructions introduced in RV64I
Browse files Browse the repository at this point in the history
As discussed in the RFC 
<http://lists.llvm.org/pipermail/llvm-dev/2018-October/126690.html>, 64-bit 
RISC-V has i64 as the only legal integer type.  This patch introduces patterns 
to support codegen of the new instructions 
introduced in RV64I: addiw, addiw, subw, sllw, slliw, srlw, srliw, sraw, 
sraiw, ld, sd.

Custom selection code is needed for srliw as SimplifyDemandedBits will remove 
lower bits from the mask, meaning the obvious pattern won't work:

def : Pat<(sext_inreg (srl (and GPR:$rs1, 0xffffffff), uimm5:$shamt), i32),
          (SRLIW GPR:$rs1, uimm5:$shamt)>;
This is sufficient to compile and execute all of the GCC torture suite for 
RV64I other than those files using frameaddr or returnaddr intrinsics 
(LegalizeDAG doesn't know how to promote the operands - a future patch 
addresses this).

When promoting i32 sltu/sltiu operands, it would be more efficient to use 
sign-extension rather than zero-extension for RV64. A future patch adds a hook 
to allow this.

Differential Revision: https://reviews.llvm.org/D52977

llvm-svn: 347973
  • Loading branch information
asb committed Nov 30, 2018
1 parent f612fad commit bc96a98
Show file tree
Hide file tree
Showing 9 changed files with 3,005 additions and 5 deletions.
34 changes: 34 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelDAGToDAG.cpp
Expand Up @@ -85,6 +85,17 @@ static SDNode *selectImm(SelectionDAG *CurDAG, const SDLoc &DL, int64_t Imm,
return Result;
}

// Returns true if the Node is an ISD::AND with a constant argument. If so,
// set Mask to that constant value.
static bool isConstantMask(SDNode *Node, uint64_t &Mask) {
if (Node->getOpcode() == ISD::AND &&
Node->getOperand(1).getOpcode() == ISD::Constant) {
Mask = cast<ConstantSDNode>(Node->getOperand(1))->getZExtValue();
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 @@ -123,6 +134,29 @@ void RISCVDAGToDAGISel::Select(SDNode *Node) {
ReplaceNode(Node, CurDAG->getMachineNode(RISCV::ADDI, DL, VT, TFI, Imm));
return;
}
case ISD::SRL: {
if (!Subtarget->is64Bit())
break;
SDValue Op0 = Node->getOperand(0);
SDValue Op1 = Node->getOperand(1);
uint64_t Mask;
// Match (srl (and val, mask), imm) where the result would be a
// zero-extended 32-bit integer. i.e. the mask is 0xffffffff or the result
// is equivalent to this (SimplifyDemandedBits may have removed lower bits
// from the mask that aren't necessary due to the right-shifting).
if (Op1.getOpcode() == ISD::Constant &&
isConstantMask(Op0.getNode(), Mask)) {
uint64_t ShAmt = cast<ConstantSDNode>(Op1.getNode())->getZExtValue();

if ((Mask | maskTrailingOnes<uint64_t>(ShAmt)) == 0xffffffff) {
SDValue ShAmtVal =
CurDAG->getTargetConstant(ShAmt, SDLoc(Node), XLenVT);
CurDAG->SelectNodeTo(Node, RISCV::SRLIW, XLenVT, Op0.getOperand(0),
ShAmtVal);
return;
}
}
}
}

// Select the default instruction.
Expand Down
66 changes: 65 additions & 1 deletion llvm/lib/Target/RISCV/RISCVInstrInfo.td
Expand Up @@ -211,6 +211,9 @@ def immbottomxlenset : ImmLeaf<XLenVT, [{
return countTrailingOnes<uint64_t>(Imm) >= 6;
return countTrailingOnes<uint64_t>(Imm) >= 5;
}]>;
def immshifti32 : ImmLeaf<XLenVT, [{
return countTrailingOnes<uint64_t>(Imm) >= 5;
}]>;

// Addressing modes.
// Necessary because a frameindex can't be matched directly in a pattern.
Expand Down Expand Up @@ -815,7 +818,7 @@ defm : LdPat<sextloadi8, LB>;
defm : LdPat<extloadi8, LB>;
defm : LdPat<sextloadi16, LH>;
defm : LdPat<extloadi16, LH>;
defm : LdPat<load, LW>;
defm : LdPat<load, LW>, Requires<[IsRV32]>;
defm : LdPat<zextloadi8, LBU>;
defm : LdPat<zextloadi16, LHU>;

Expand Down Expand Up @@ -866,6 +869,67 @@ def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
[(CallSeqEnd timm:$amt1, timm:$amt2)]>;
} // Defs = [X2], Uses = [X2]

/// RV64 patterns

def assertzexti32 : PatFrag<(ops node:$src), (assertzext node:$src), [{
return cast<VTSDNode>(N->getOperand(1))->getVT() == MVT::i32;
}]>;
def zexti32 : PatFrags<(ops node:$src),
[(and node:$src, 0xffffffff),
(assertzexti32 node:$src)]>;

let Predicates = [IsRV64] in {

/// sext and zext

def : Pat<(sext_inreg GPR:$rs1, i32), (ADDIW GPR:$rs1, 0)>;
def : Pat<(and GPR:$rs1, 0xffffffff), (SRLI (SLLI GPR:$rs1, 32), 32)>;

/// ALU operations

def : Pat<(sext_inreg (add GPR:$rs1, GPR:$rs2), i32),
(ADDW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sext_inreg (add GPR:$rs1, simm12:$imm12), i32),
(ADDIW GPR:$rs1, simm12:$imm12)>;
def : Pat<(sext_inreg (sub GPR:$rs1, GPR:$rs2), i32),
(SUBW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sext_inreg (shl GPR:$rs1, uimm5:$shamt), i32),
(SLLIW GPR:$rs1, uimm5:$shamt)>;
// (srl (zexti32 ...), uimm5:$shamt) is matched with custom code due to the
// need to undo manipulation of the mask value performed by DAGCombine.
def : Pat<(sra (sext_inreg GPR:$rs1, i32), uimm5:$shamt),
(SRAIW GPR:$rs1, uimm5:$shamt)>;

def : Pat<(sext_inreg (shl GPR:$rs1, GPR:$rs2), i32),
(SLLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sext_inreg (shl GPR:$rs1, (and GPR:$rs2, immshifti32)), i32),
(SLLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(srl (zexti32 GPR:$rs1), GPR:$rs2),
(SRLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(srl (zexti32 GPR:$rs1), (and GPR:$rs2, immshifti32)),
(SRLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sext_inreg (srl (zexti32 GPR:$rs1), GPR:$rs2), i32),
(SRLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sext_inreg (srl (zexti32 GPR:$rs1), (and GPR:$rs2, immshifti32)), i32),
(SRLW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sra (sext_inreg GPR:$rs1, i32), GPR:$rs2),
(SRAW GPR:$rs1, GPR:$rs2)>;
def : Pat<(sra (sext_inreg GPR:$rs1, i32), (and GPR:$rs2, immshifti32)),
(SRAW GPR:$rs1, GPR:$rs2)>;

/// Loads

defm : LdPat<sextloadi32, LW>;
defm : LdPat<extloadi32, LW>;
defm : LdPat<zextloadi32, LWU>;
defm : LdPat<load, LD>;

/// Stores

defm : StPat<truncstorei32, SW, GPR>;
defm : StPat<store, SD, GPR>;
} // Predicates = [IsRV64]

//===----------------------------------------------------------------------===//
// Standard extensions
//===----------------------------------------------------------------------===//
Expand Down
119 changes: 119 additions & 0 deletions llvm/test/CodeGen/RISCV/alu16.ll
@@ -1,6 +1,8 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I
; RUN: llc -mtriple=riscv64 -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV64I

; These tests are identical to those in alu32.ll but operate on i16. They check
; that legalisation of these non-native types doesn't introduce unnecessary
Expand All @@ -11,6 +13,11 @@ define i16 @addi(i16 %a) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: addi a0, a0, 1
; RV32I-NEXT: ret
;
; RV64I-LABEL: addi:
; RV64I: # %bb.0:
; RV64I-NEXT: addi a0, a0, 1
; RV64I-NEXT: ret
%1 = add i16 %a, 1
ret i16 %1
}
Expand All @@ -22,6 +29,13 @@ define i16 @slti(i16 %a) nounwind {
; RV32I-NEXT: srai a0, a0, 16
; RV32I-NEXT: slti a0, a0, 2
; RV32I-NEXT: ret
;
; RV64I-LABEL: slti:
; RV64I: # %bb.0:
; RV64I-NEXT: slli a0, a0, 48
; RV64I-NEXT: srai a0, a0, 48
; RV64I-NEXT: slti a0, a0, 2
; RV64I-NEXT: ret
%1 = icmp slt i16 %a, 2
%2 = zext i1 %1 to i16
ret i16 %2
Expand All @@ -35,6 +49,14 @@ define i16 @sltiu(i16 %a) nounwind {
; RV32I-NEXT: and a0, a0, a1
; RV32I-NEXT: sltiu a0, a0, 3
; RV32I-NEXT: ret
;
; RV64I-LABEL: sltiu:
; RV64I: # %bb.0:
; RV64I-NEXT: lui a1, 16
; RV64I-NEXT: addiw a1, a1, -1
; RV64I-NEXT: and a0, a0, a1
; RV64I-NEXT: sltiu a0, a0, 3
; RV64I-NEXT: ret
%1 = icmp ult i16 %a, 3
%2 = zext i1 %1 to i16
ret i16 %2
Expand All @@ -45,6 +67,11 @@ define i16 @xori(i16 %a) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: xori a0, a0, 4
; RV32I-NEXT: ret
;
; RV64I-LABEL: xori:
; RV64I: # %bb.0:
; RV64I-NEXT: xori a0, a0, 4
; RV64I-NEXT: ret
%1 = xor i16 %a, 4
ret i16 %1
}
Expand All @@ -54,6 +81,11 @@ define i16 @ori(i16 %a) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: ori a0, a0, 5
; RV32I-NEXT: ret
;
; RV64I-LABEL: ori:
; RV64I: # %bb.0:
; RV64I-NEXT: ori a0, a0, 5
; RV64I-NEXT: ret
%1 = or i16 %a, 5
ret i16 %1
}
Expand All @@ -63,6 +95,11 @@ define i16 @andi(i16 %a) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: andi a0, a0, 6
; RV32I-NEXT: ret
;
; RV64I-LABEL: andi:
; RV64I: # %bb.0:
; RV64I-NEXT: andi a0, a0, 6
; RV64I-NEXT: ret
%1 = and i16 %a, 6
ret i16 %1
}
Expand All @@ -72,6 +109,11 @@ define i16 @slli(i16 %a) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: slli a0, a0, 7
; RV32I-NEXT: ret
;
; RV64I-LABEL: slli:
; RV64I: # %bb.0:
; RV64I-NEXT: slli a0, a0, 7
; RV64I-NEXT: ret
%1 = shl i16 %a, 7
ret i16 %1
}
Expand All @@ -84,6 +126,14 @@ define i16 @srli(i16 %a) nounwind {
; RV32I-NEXT: and a0, a0, a1
; RV32I-NEXT: srli a0, a0, 6
; RV32I-NEXT: ret
;
; RV64I-LABEL: srli:
; RV64I: # %bb.0:
; RV64I-NEXT: lui a1, 16
; RV64I-NEXT: addiw a1, a1, -64
; RV64I-NEXT: and a0, a0, a1
; RV64I-NEXT: srli a0, a0, 6
; RV64I-NEXT: ret
%1 = lshr i16 %a, 6
ret i16 %1
}
Expand All @@ -94,6 +144,12 @@ define i16 @srai(i16 %a) nounwind {
; RV32I-NEXT: slli a0, a0, 16
; RV32I-NEXT: srai a0, a0, 25
; RV32I-NEXT: ret
;
; RV64I-LABEL: srai:
; RV64I: # %bb.0:
; RV64I-NEXT: slli a0, a0, 48
; RV64I-NEXT: srai a0, a0, 57
; RV64I-NEXT: ret
%1 = ashr i16 %a, 9
ret i16 %1
}
Expand All @@ -104,6 +160,11 @@ define i16 @add(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: add a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: add:
; RV64I: # %bb.0:
; RV64I-NEXT: add a0, a0, a1
; RV64I-NEXT: ret
%1 = add i16 %a, %b
ret i16 %1
}
Expand All @@ -113,6 +174,11 @@ define i16 @sub(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: sub a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: sub:
; RV64I: # %bb.0:
; RV64I-NEXT: sub a0, a0, a1
; RV64I-NEXT: ret
%1 = sub i16 %a, %b
ret i16 %1
}
Expand All @@ -122,6 +188,11 @@ define i16 @sll(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: sll a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: sll:
; RV64I: # %bb.0:
; RV64I-NEXT: sll a0, a0, a1
; RV64I-NEXT: ret
%1 = shl i16 %a, %b
ret i16 %1
}
Expand All @@ -135,6 +206,15 @@ define i16 @slt(i16 %a, i16 %b) nounwind {
; RV32I-NEXT: srai a0, a0, 16
; RV32I-NEXT: slt a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: slt:
; RV64I: # %bb.0:
; RV64I-NEXT: slli a1, a1, 48
; RV64I-NEXT: srai a1, a1, 48
; RV64I-NEXT: slli a0, a0, 48
; RV64I-NEXT: srai a0, a0, 48
; RV64I-NEXT: slt a0, a0, a1
; RV64I-NEXT: ret
%1 = icmp slt i16 %a, %b
%2 = zext i1 %1 to i16
ret i16 %2
Expand All @@ -149,6 +229,15 @@ define i16 @sltu(i16 %a, i16 %b) nounwind {
; RV32I-NEXT: and a0, a0, a2
; RV32I-NEXT: sltu a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: sltu:
; RV64I: # %bb.0:
; RV64I-NEXT: lui a2, 16
; RV64I-NEXT: addiw a2, a2, -1
; RV64I-NEXT: and a1, a1, a2
; RV64I-NEXT: and a0, a0, a2
; RV64I-NEXT: sltu a0, a0, a1
; RV64I-NEXT: ret
%1 = icmp ult i16 %a, %b
%2 = zext i1 %1 to i16
ret i16 %2
Expand All @@ -159,6 +248,11 @@ define i16 @xor(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: xor a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: xor:
; RV64I: # %bb.0:
; RV64I-NEXT: xor a0, a0, a1
; RV64I-NEXT: ret
%1 = xor i16 %a, %b
ret i16 %1
}
Expand All @@ -171,6 +265,14 @@ define i16 @srl(i16 %a, i16 %b) nounwind {
; RV32I-NEXT: and a0, a0, a2
; RV32I-NEXT: srl a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: srl:
; RV64I: # %bb.0:
; RV64I-NEXT: lui a2, 16
; RV64I-NEXT: addiw a2, a2, -1
; RV64I-NEXT: and a0, a0, a2
; RV64I-NEXT: srl a0, a0, a1
; RV64I-NEXT: ret
%1 = lshr i16 %a, %b
ret i16 %1
}
Expand All @@ -182,6 +284,13 @@ define i16 @sra(i16 %a, i16 %b) nounwind {
; RV32I-NEXT: srai a0, a0, 16
; RV32I-NEXT: sra a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: sra:
; RV64I: # %bb.0:
; RV64I-NEXT: slli a0, a0, 48
; RV64I-NEXT: srai a0, a0, 48
; RV64I-NEXT: sra a0, a0, a1
; RV64I-NEXT: ret
%1 = ashr i16 %a, %b
ret i16 %1
}
Expand All @@ -191,6 +300,11 @@ define i16 @or(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: or a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: or:
; RV64I: # %bb.0:
; RV64I-NEXT: or a0, a0, a1
; RV64I-NEXT: ret
%1 = or i16 %a, %b
ret i16 %1
}
Expand All @@ -200,6 +314,11 @@ define i16 @and(i16 %a, i16 %b) nounwind {
; RV32I: # %bb.0:
; RV32I-NEXT: and a0, a0, a1
; RV32I-NEXT: ret
;
; RV64I-LABEL: and:
; RV64I: # %bb.0:
; RV64I-NEXT: and a0, a0, a1
; RV64I-NEXT: ret
%1 = and i16 %a, %b
ret i16 %1
}

0 comments on commit bc96a98

Please sign in to comment.