Index: lib/Target/RISCV/RISCVFrameLowering.h =================================================================== --- lib/Target/RISCV/RISCVFrameLowering.h +++ lib/Target/RISCV/RISCVFrameLowering.h @@ -30,6 +30,12 @@ void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override; bool hasFP(const MachineFunction &MF) const override; + + MachineBasicBlock::iterator + eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, + MachineBasicBlock::iterator MI) const override { + return MBB.erase(MI); + } }; } #endif Index: lib/Target/RISCV/RISCVISelLowering.h =================================================================== --- lib/Target/RISCV/RISCVISelLowering.h +++ lib/Target/RISCV/RISCVISelLowering.h @@ -24,7 +24,8 @@ namespace RISCVISD { enum NodeType : unsigned { FIRST_NUMBER = ISD::BUILTIN_OP_END, - RET_FLAG + RET_FLAG, + CALL }; } @@ -52,6 +53,8 @@ const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const override; + SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const override; bool shouldConvertConstantLoadToIntImm(const APInt &Imm, Type *Ty) const override { return true; Index: lib/Target/RISCV/RISCVISelLowering.cpp =================================================================== --- lib/Target/RISCV/RISCVISelLowering.cpp +++ lib/Target/RISCV/RISCVISelLowering.cpp @@ -150,6 +150,135 @@ return Chain; } +// Lower a call to a callseq_start + CALL + callseq_end chain, and add input +// and output parameter nodes. +SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI, + SmallVectorImpl &InVals) const { + SelectionDAG &DAG = CLI.DAG; + SDLoc &DL = CLI.DL; + SmallVectorImpl &Outs = CLI.Outs; + SmallVectorImpl &OutVals = CLI.OutVals; + SmallVectorImpl &Ins = CLI.Ins; + SDValue Chain = CLI.Chain; + SDValue Callee = CLI.Callee; + CLI.IsTailCall = false; + CallingConv::ID CallConv = CLI.CallConv; + bool IsVarArg = CLI.IsVarArg; + EVT PtrVT = getPointerTy(DAG.getDataLayout()); + + if (IsVarArg) { + report_fatal_error("LowerCall with varargs not implemented"); + } + + MachineFunction &MF = DAG.getMachineFunction(); + + // Analyze the operands of the call, assigning locations to each operand. + SmallVector ArgLocs; + CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext()); + ArgCCInfo.AnalyzeCallOperands(Outs, CC_RISCV32); + + // Get a count of how many bytes are to be pushed on the stack. + unsigned NumBytes = ArgCCInfo.getNextStackOffset(); + + for (auto &Arg : Outs) { + if (!Arg.Flags.isByVal()) + continue; + report_fatal_error("Passing arguments byval not yet implemented"); + } + + Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL); + + // Copy argument values to their designated locations. + SmallVector, 8> RegsToPass; + SDValue StackPtr; + for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) { + CCValAssign &VA = ArgLocs[I]; + SDValue ArgValue = OutVals[I]; + + // Promote the value if needed. + // For now, only handle fully promoted arguments. + switch (VA.getLocInfo()) { + case CCValAssign::Full: + break; + default: + llvm_unreachable("Unknown loc info!"); + } + + if (VA.isRegLoc()) { + // Queue up the argument copies and emit them at the end. + RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue)); + } else { + assert(VA.isMemLoc() && "Argument not register or memory"); + report_fatal_error("Passing arguments via the stack not yet implemented"); + } + } + + SDValue Glue; + + // Build a sequence of copy-to-reg nodes, chained and glued together. + for (auto &Reg : RegsToPass) { + Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, Glue); + Glue = Chain.getValue(1); + } + + if (isa(Callee)) { + Callee = lowerGlobalAddress(Callee, DAG); + } else if (isa(Callee)) { + report_fatal_error( + "lowerExternalSymbol, needed for lowerCall, not yet handled"); + } + + // The first call operand is the chain and the second is the target address. + SmallVector Ops; + Ops.push_back(Chain); + Ops.push_back(Callee); + + // Add argument registers to the end of the list so that they are + // known live into the call. + for (auto &Reg : RegsToPass) + Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType())); + + // Add a register mask operand representing the call-preserved registers. + const TargetRegisterInfo *TRI = Subtarget->getRegisterInfo(); + const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv); + assert(Mask && "Missing call preserved mask for calling convention"); + Ops.push_back(DAG.getRegisterMask(Mask)); + + // Glue the call to the argument copies, if any. + if (Glue.getNode()) + Ops.push_back(Glue); + + // Emit the call. + SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); + Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops); + Glue = Chain.getValue(1); + + // Mark the end of the call, which is glued to the call itself. + Chain = DAG.getCALLSEQ_END(Chain, + DAG.getConstant(NumBytes, DL, PtrVT, true), + DAG.getConstant(0, DL, PtrVT, true), + Glue, DL); + Glue = Chain.getValue(1); + + // Assign locations to each value returned by this call. + SmallVector RVLocs; + CCState RetCCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); + RetCCInfo.AnalyzeCallResult(Ins, RetCC_RISCV32); + + // Copy all of the result registers out of their specified physreg. + for (auto &VA : RVLocs) { + // Copy the value out, gluing the copy to the end of the call sequence. + SDValue RetValue = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(), + VA.getLocVT(), Glue); + Chain = RetValue.getValue(1); + Glue = RetValue.getValue(2); + + InVals.push_back(Chain.getValue(0)); + } + + return Chain; +} + SDValue RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, @@ -200,6 +329,8 @@ break; case RISCVISD::RET_FLAG: return "RISCVISD::RET_FLAG"; + case RISCVISD::CALL: + return "RISCVISD::CALL"; } return nullptr; } Index: lib/Target/RISCV/RISCVInstrInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.cpp +++ lib/Target/RISCV/RISCVInstrInfo.cpp @@ -28,7 +28,7 @@ using namespace llvm; -RISCVInstrInfo::RISCVInstrInfo() : RISCVGenInstrInfo() {} +RISCVInstrInfo::RISCVInstrInfo() : RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP) {} void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB, MachineBasicBlock::iterator Position, Index: lib/Target/RISCV/RISCVInstrInfo.td =================================================================== --- lib/Target/RISCV/RISCVInstrInfo.td +++ lib/Target/RISCV/RISCVInstrInfo.td @@ -13,8 +13,22 @@ include "RISCVInstrFormats.td" +def SDT_RISCVCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, + SDTCisVT<1, i32>]>; +def SDT_RISCVCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, + SDTCisVT<1, i32>]>; +def SDT_RISCVCall : SDTypeProfile<0, -1, [SDTCisVT<0, i32>]>; + + +def Call : SDNode<"RISCVISD::CALL", SDT_RISCVCall, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue, + SDNPVariadic]>; def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; +def CallSeqStart : SDNode<"ISD::CALLSEQ_START", SDT_RISCVCallSeqStart, + [SDNPHasChain, SDNPOutGlue]>; +def CallSeqEnd : SDNode<"ISD::CALLSEQ_END", SDT_RISCVCallSeqEnd, + [SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>; // Operands class ImmAsmOperand : AsmOperandClass { @@ -110,9 +124,11 @@ def AUIPC : FU<0b0010111, (outs GPR:$rd), (ins uimm20:$imm20), "auipc\t$rd, $imm20", []>; +let isCall = 1 in def JAL : FUJ<0b1101111, (outs GPR:$rd), (ins simm21_lsb0:$imm20), "jal\t$rd, $imm20", []>; +let isCall = 1 in def JALR : FI<0b000, 0b1100111, (outs GPR:$rd), (ins GPR:$rs1, simm12:$imm12), "jalr\t$rd, $rs1, $imm12", []>; } @@ -318,6 +334,11 @@ def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>, PseudoInstExpansion<(JAL X0_32, simm21_lsb0:$imm20)>; +let isCall = 1, Defs=[X1_32] in +def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>, + PseudoInstExpansion<(JALR X1_32, GPR:$rs1, 0)>; + + let isBarrier = 1, isReturn = 1, isTerminator = 1 in def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>, PseudoInstExpansion<(JALR X0_32, X1_32, 0)>; @@ -347,3 +368,13 @@ defm : StPat; defm : StPat; defm : StPat; + +/// Other pseudo-instructions + +// Pessimistically assume the stack pointer will be clobbered +let Defs = [X2_32], Uses = [X2_32] in { + def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(CallSeqStart timm:$amt1, timm:$amt2)]>; + def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), + [(CallSeqEnd timm:$amt1, timm:$amt2)]>; +} Index: lib/Target/RISCV/RISCVMCInstLower.cpp =================================================================== --- lib/Target/RISCV/RISCVMCInstLower.cpp +++ lib/Target/RISCV/RISCVMCInstLower.cpp @@ -69,6 +69,9 @@ return false; MCOp = MCOperand::createReg(MO.getReg()); break; + case MachineOperand::MO_RegisterMask: + // Regmasks are like implicit defs. + return false; case MachineOperand::MO_Immediate: MCOp = MCOperand::createImm(MO.getImm()); break; Index: lib/Target/RISCV/RISCVRegisterInfo.h =================================================================== --- lib/Target/RISCV/RISCVRegisterInfo.h +++ lib/Target/RISCV/RISCVRegisterInfo.h @@ -25,6 +25,9 @@ RISCVRegisterInfo(); + const uint32_t *getCallPreservedMask(const MachineFunction &MF, + CallingConv::ID) const override; + const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override; BitVector getReservedRegs(const MachineFunction &MF) const override; Index: lib/Target/RISCV/RISCVRegisterInfo.cpp =================================================================== --- lib/Target/RISCV/RISCVRegisterInfo.cpp +++ lib/Target/RISCV/RISCVRegisterInfo.cpp @@ -87,3 +87,9 @@ unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const { return RISCV::X8_32; } + +const uint32_t * +RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & /*MF*/, + CallingConv::ID /*CC*/) const { + return CSR_RegMask; +} Index: test/CodeGen/RISCV/calls.ll =================================================================== --- /dev/null +++ test/CodeGen/RISCV/calls.ll @@ -0,0 +1,35 @@ +; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s | FileCheck %s + +declare i32 @external_function(i32) nounwind + +define i32 @test_call_external(i32 %a) { +; CHECK-LABEL: test_call_external: +; CHECK: lui a1, %hi(external_function) +; CHECK: addi a1, a1, %lo(external_function) +; CHECK: jalr ra, a1, 0 + %1 = call i32 @external_function(i32 %a) nounwind + ret i32 %1 +} + +define i32 @defined_function(i32 %a) nounwind noinline { + %1 = add i32 %a, 1 + ret i32 %1 +} + +define i32 @test_call_defined(i32 %a) { +; CHECK-LABEL: test_call_defined: +; CHECK: lui a1, %hi(defined_function) +; CHECK: addi a1, a1, %lo(defined_function) +; CHECK: jalr ra, a1, 0 + %1 = call i32 @defined_function(i32 %a) nounwind + ret i32 %1 +} + +define i32 @test_call_indirect(i32 (i32)* %a, i32 %b) { +; CHECK-LABEL: test_call_indirect: +; CHECK: addi a2, a0, 0 +; CHECK: addi a0, a1, 0 +; CHECK: jalr ra, a2, 0 + %1 = call i32 %a(i32 %b) + ret i32 %1 +}