Skip to content

Commit

Permalink
[RISCV] Support and tests for a variety of additional LLVM IR constructs
Browse files Browse the repository at this point in the history
Previous patches primarily ensured that codegen was possible for the standard
RISC-V instructions. However, there are a number of IR inputs that wouldn't be
appropriately lowered. This patch both adds test cases and supports lowering
for a number of these cases:
* Improved sext/zext/trunc support
* Support for setcc variants that don't map directly to RISC-V instructions
* Lowering mul, and hence support for external symbols
* addc, adde, subc, sube
* mulhs, srem, mulhu, urem, udiv, sdiv
* {srl,sra,shl}_parts
* brind
* br_jt
* bswap, ctlz, cttz, ctpop
* rotl, rotr
* BlockAddress operands

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

llvm-svn: 318737
  • Loading branch information
asb committed Nov 21, 2017
1 parent dca72fc commit ffc435e
Show file tree
Hide file tree
Showing 17 changed files with 1,566 additions and 15 deletions.
106 changes: 91 additions & 15 deletions llvm/lib/Target/RISCV/RISCVISelLowering.cpp
Expand Up @@ -53,17 +53,54 @@ RISCVTargetLowering::RISCVTargetLowering(const TargetMachine &TM,
setLoadExtAction(N, XLenVT, MVT::i1, Promote);

// TODO: add all necessary setOperationAction calls.
setOperationAction(ISD::GlobalAddress, XLenVT, Custom);

setOperationAction(ISD::BR_JT, MVT::Other, Expand);
setOperationAction(ISD::BR_CC, XLenVT, Expand);
setOperationAction(ISD::SELECT, XLenVT, Custom);
setOperationAction(ISD::SELECT_CC, XLenVT, Expand);

for (auto VT : {MVT::i1, MVT::i8, MVT::i16})
setOperationAction(ISD::SIGN_EXTEND_INREG, VT, Expand);

setOperationAction(ISD::ADDC, XLenVT, Expand);
setOperationAction(ISD::ADDE, XLenVT, Expand);
setOperationAction(ISD::SUBC, XLenVT, Expand);
setOperationAction(ISD::SUBE, XLenVT, Expand);

setOperationAction(ISD::SREM, XLenVT, Expand);
setOperationAction(ISD::SDIVREM, XLenVT, Expand);
setOperationAction(ISD::SDIV, XLenVT, Expand);
setOperationAction(ISD::UREM, XLenVT, Expand);
setOperationAction(ISD::UDIVREM, XLenVT, Expand);
setOperationAction(ISD::UDIV, XLenVT, Expand);

setOperationAction(ISD::MUL, XLenVT, Expand);
setOperationAction(ISD::SMUL_LOHI, XLenVT, Expand);
setOperationAction(ISD::UMUL_LOHI, XLenVT, Expand);
setOperationAction(ISD::MULHS, XLenVT, Expand);
setOperationAction(ISD::MULHU, XLenVT, Expand);

setOperationAction(ISD::SHL_PARTS, XLenVT, Expand);
setOperationAction(ISD::SRL_PARTS, XLenVT, Expand);
setOperationAction(ISD::SRA_PARTS, XLenVT, Expand);

setOperationAction(ISD::ROTL, XLenVT, Expand);
setOperationAction(ISD::ROTR, XLenVT, Expand);
setOperationAction(ISD::BSWAP, XLenVT, Expand);
setOperationAction(ISD::CTTZ, XLenVT, Expand);
setOperationAction(ISD::CTLZ, XLenVT, Expand);
setOperationAction(ISD::CTPOP, XLenVT, Expand);

setOperationAction(ISD::GlobalAddress, XLenVT, Custom);
setOperationAction(ISD::BlockAddress, XLenVT, Custom);

setBooleanContents(ZeroOrOneBooleanContent);

// Function alignments (log2).
setMinFunctionAlignment(3);
setPrefFunctionAlignment(3);

// Effectively disable jump table generation.
setMinimumJumpTableEntries(INT_MAX);
}

// Changes the condition code and swaps operands if necessary, so the SetCC
Expand Down Expand Up @@ -112,6 +149,8 @@ SDValue RISCVTargetLowering::LowerOperation(SDValue Op,
report_fatal_error("unimplemented operand");
case ISD::GlobalAddress:
return lowerGlobalAddress(Op, DAG);
case ISD::BlockAddress:
return lowerBlockAddress(Op, DAG);
case ISD::SELECT:
return lowerSELECT(Op, DAG);
}
Expand All @@ -125,18 +164,56 @@ SDValue RISCVTargetLowering::lowerGlobalAddress(SDValue Op,
const GlobalValue *GV = N->getGlobal();
int64_t Offset = N->getOffset();

if (!isPositionIndependent() && !Subtarget.is64Bit()) {
SDValue GAHi =
DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_HI);
SDValue GALo =
DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_LO);
SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, GAHi), 0);
SDValue MNLo =
SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, GALo), 0);
return MNLo;
} else {
if (isPositionIndependent() || Subtarget.is64Bit())
report_fatal_error("Unable to lowerGlobalAddress");
}

SDValue GAHi =
DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_HI);
SDValue GALo =
DAG.getTargetGlobalAddress(GV, DL, Ty, Offset, RISCVII::MO_LO);
SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, GAHi), 0);
SDValue MNLo =
SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, GALo), 0);
return MNLo;
}

SDValue RISCVTargetLowering::lowerBlockAddress(SDValue Op,
SelectionDAG &DAG) const {
SDLoc DL(Op);
EVT Ty = Op.getValueType();
BlockAddressSDNode *N = cast<BlockAddressSDNode>(Op);
const BlockAddress *BA = N->getBlockAddress();
int64_t Offset = N->getOffset();

if (isPositionIndependent() || Subtarget.is64Bit())
report_fatal_error("Unable to lowerBlockAddress");

SDValue BAHi = DAG.getTargetBlockAddress(BA, Ty, Offset, RISCVII::MO_HI);
SDValue BALo = DAG.getTargetBlockAddress(BA, Ty, Offset, RISCVII::MO_LO);
SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, BAHi), 0);
SDValue MNLo =
SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, BALo), 0);
return MNLo;
}

SDValue RISCVTargetLowering::lowerExternalSymbol(SDValue Op,
SelectionDAG &DAG) const {
SDLoc DL(Op);
EVT Ty = Op.getValueType();
ExternalSymbolSDNode *N = cast<ExternalSymbolSDNode>(Op);
const char *Sym = N->getSymbol();

// TODO: should also handle gp-relative loads.

if (isPositionIndependent() || Subtarget.is64Bit())
report_fatal_error("Unable to lowerExternalSymbol");

SDValue GAHi = DAG.getTargetExternalSymbol(Sym, Ty, RISCVII::MO_HI);
SDValue GALo = DAG.getTargetExternalSymbol(Sym, Ty, RISCVII::MO_LO);
SDValue MNHi = SDValue(DAG.getMachineNode(RISCV::LUI, DL, Ty, GAHi), 0);
SDValue MNLo =
SDValue(DAG.getMachineNode(RISCV::ADDI, DL, Ty, MNHi, GALo), 0);
return MNLo;
}

SDValue RISCVTargetLowering::lowerSELECT(SDValue Op, SelectionDAG &DAG) const {
Expand Down Expand Up @@ -369,8 +446,7 @@ SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
if (isa<GlobalAddressSDNode>(Callee)) {
Callee = lowerGlobalAddress(Callee, DAG);
} else if (isa<ExternalSymbolSDNode>(Callee)) {
report_fatal_error(
"lowerExternalSymbol, needed for lowerCall, not yet handled");
Callee = lowerExternalSymbol(Callee, DAG);
}

// The first call operand is the chain and the second is the target address.
Expand Down
2 changes: 2 additions & 0 deletions llvm/lib/Target/RISCV/RISCVISelLowering.h
Expand Up @@ -65,6 +65,8 @@ class RISCVTargetLowering : public TargetLowering {
return true;
}
SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerExternalSymbol(SDValue Op, SelectionDAG &DAG) const;
SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const;
};
}
Expand Down
20 changes: 20 additions & 0 deletions llvm/lib/Target/RISCV/RISCVInstrInfo.td
Expand Up @@ -328,6 +328,17 @@ def : PatGprSimm12<setlt, SLTI>;
def : PatGprGpr<setult, SLTU>;
def : PatGprSimm12<setult, SLTIU>;

// Define pattern expansions for setcc operations that aren't directly
// handled by a RISC-V instruction.
def : Pat<(seteq GPR:$rs1, GPR:$rs2), (SLTIU (XOR GPR:$rs1, GPR:$rs2), 1)>;
def : Pat<(setne GPR:$rs1, GPR:$rs2), (SLTU X0, (XOR GPR:$rs1, GPR:$rs2))>;
def : Pat<(setugt GPR:$rs1, GPR:$rs2), (SLTU GPR:$rs2, GPR:$rs1)>;
def : Pat<(setuge GPR:$rs1, GPR:$rs2), (XORI (SLTU GPR:$rs1, GPR:$rs2), 1)>;
def : Pat<(setule GPR:$rs1, GPR:$rs2), (XORI (SLTU GPR:$rs2, GPR:$rs1), 1)>;
def : Pat<(setgt GPR:$rs1, GPR:$rs2), (SLT GPR:$rs2, GPR:$rs1)>;
def : Pat<(setge GPR:$rs1, GPR:$rs2), (XORI (SLT GPR:$rs1, GPR:$rs2), 1)>;
def : Pat<(setle GPR:$rs1, GPR:$rs2), (XORI (SLT GPR:$rs2, GPR:$rs1), 1)>;

let usesCustomInserter = 1 in
def Select_GPR_Using_CC_GPR
: Pseudo<(outs GPR:$dst),
Expand Down Expand Up @@ -370,6 +381,15 @@ def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>,
PseudoInstExpansion<(JAL X0, simm21_lsb0:$imm20)>;

let isCall = 1, Defs=[X1] in
let isBarrier = 1, isBranch = 1, isIndirectBranch = 1, isTerminator = 1 in
def PseudoBRIND : Pseudo<(outs), (ins GPR:$rs1, simm12:$imm12), []>,
PseudoInstExpansion<(JALR X0, GPR:$rs1, simm12:$imm12)>;

def : Pat<(brind GPR:$rs1), (PseudoBRIND GPR:$rs1, 0)>;
def : Pat<(brind (add GPR:$rs1, simm12:$imm12)),
(PseudoBRIND GPR:$rs1, simm12:$imm12)>;

let isCall = 1, Defs = [X1] in
def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>,
PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;

Expand Down
8 changes: 8 additions & 0 deletions llvm/lib/Target/RISCV/RISCVMCInstLower.cpp
Expand Up @@ -81,6 +81,14 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
case MachineOperand::MO_GlobalAddress:
MCOp = lowerSymbolOperand(MO, AP.getSymbol(MO.getGlobal()), AP);
break;
case MachineOperand::MO_BlockAddress:
MCOp = lowerSymbolOperand(
MO, AP.GetBlockAddressSymbol(MO.getBlockAddress()), AP);
break;
case MachineOperand::MO_ExternalSymbol:
MCOp = lowerSymbolOperand(
MO, AP.GetExternalSymbolSymbol(MO.getSymbolName()), AP);
break;
}
return true;
}
Expand Down
30 changes: 30 additions & 0 deletions llvm/test/CodeGen/RISCV/addc-adde-sube-subc.ll
@@ -0,0 +1,30 @@
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefix=RV32I %s

; Ensure that the ISDOpcodes ADDC, ADDE, SUBC, SUBE are handled correctly

define i64 @addc_adde(i64 %a, i64 %b) {
; RV32I-LABEL: addc_adde:
; RV32I: # BB#0:
; RV32I-NEXT: add a1, a1, a3
; RV32I-NEXT: add a2, a0, a2
; RV32I-NEXT: sltu a0, a2, a0
; RV32I-NEXT: add a1, a1, a0
; RV32I-NEXT: addi a0, a2, 0
; RV32I-NEXT: jalr zero, ra, 0
%1 = add i64 %a, %b
ret i64 %1
}

define i64 @subc_sube(i64 %a, i64 %b) {
; RV32I-LABEL: subc_sube:
; RV32I: # BB#0:
; RV32I-NEXT: sub a1, a1, a3
; RV32I-NEXT: sltu a3, a0, a2
; RV32I-NEXT: sub a1, a1, a3
; RV32I-NEXT: sub a0, a0, a2
; RV32I-NEXT: jalr zero, ra, 0
%1 = sub i64 %a, %b
ret i64 %1
}
4 changes: 4 additions & 0 deletions llvm/test/CodeGen/RISCV/alu32.ll
Expand Up @@ -2,6 +2,10 @@
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
; RUN: | FileCheck %s -check-prefix=RV32I

; These tests are each targeted at a particular RISC-V ALU instruction. Other
; files in this folder exercise LLVM IR instructions that don't directly match a
; RISC-V instruction

; Register-immediate instructions

define i32 @addi(i32 %a) nounwind {
Expand Down
28 changes: 28 additions & 0 deletions llvm/test/CodeGen/RISCV/blockaddress.ll
@@ -0,0 +1,28 @@
; 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

@addr = global i8* null

define void @test_blockaddress() nounwind {
; RV32I-LABEL: test_blockaddress:
; RV32I: # BB#0:
; RV32I-NEXT: sw ra, 0(s0)
; RV32I-NEXT: lui a0, %hi(addr)
; RV32I-NEXT: addi a0, a0, %lo(addr)
; RV32I-NEXT: lui a1, %hi(.Ltmp0)
; RV32I-NEXT: addi a1, a1, %lo(.Ltmp0)
; RV32I-NEXT: sw a1, 0(a0)
; RV32I-NEXT: lw a0, 0(a0)
; RV32I-NEXT: jalr zero, a0, 0
; RV32I-NEXT: .Ltmp0: # Block address taken
; RV32I-NEXT: .LBB0_1: # %block
; RV32I-NEXT: lw ra, 0(s0)
; RV32I-NEXT: jalr zero, ra, 0
store volatile i8* blockaddress(@test_blockaddress, %block), i8** @addr
%val = load volatile i8*, i8** @addr
indirectbr i8* %val, [label %block]

block:
ret void
}

0 comments on commit ffc435e

Please sign in to comment.