Skip to content

Commit da781c7

Browse files
committed
[RISCV] Initial support for function calls
Note that this is just enough for simple function call examples to generate working code. Support for varargs etc follows in future patches. Differential Revision: https://reviews.llvm.org/D29936 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@317691 91177308-0d34-0410-b5e6-96231b3b80d8
1 parent eacca30 commit da781c7

File tree

9 files changed

+269
-4
lines changed

9 files changed

+269
-4
lines changed

lib/Target/RISCV/RISCVFrameLowering.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@ class RISCVFrameLowering : public TargetFrameLowering {
3030
void emitEpilogue(MachineFunction &MF, MachineBasicBlock &MBB) const override;
3131

3232
bool hasFP(const MachineFunction &MF) const override;
33+
34+
MachineBasicBlock::iterator
35+
eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB,
36+
MachineBasicBlock::iterator MI) const override {
37+
return MBB.erase(MI);
38+
}
3339
};
3440
}
3541
#endif

lib/Target/RISCV/RISCVISelLowering.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
108108
default:
109109
report_fatal_error("Unsupported calling convention");
110110
case CallingConv::C:
111+
case CallingConv::Fast:
111112
break;
112113
}
113114

@@ -144,6 +145,135 @@ SDValue RISCVTargetLowering::LowerFormalArguments(
144145
return Chain;
145146
}
146147

148+
// Lower a call to a callseq_start + CALL + callseq_end chain, and add input
149+
// and output parameter nodes.
150+
SDValue RISCVTargetLowering::LowerCall(CallLoweringInfo &CLI,
151+
SmallVectorImpl<SDValue> &InVals) const {
152+
SelectionDAG &DAG = CLI.DAG;
153+
SDLoc &DL = CLI.DL;
154+
SmallVectorImpl<ISD::OutputArg> &Outs = CLI.Outs;
155+
SmallVectorImpl<SDValue> &OutVals = CLI.OutVals;
156+
SmallVectorImpl<ISD::InputArg> &Ins = CLI.Ins;
157+
SDValue Chain = CLI.Chain;
158+
SDValue Callee = CLI.Callee;
159+
CLI.IsTailCall = false;
160+
CallingConv::ID CallConv = CLI.CallConv;
161+
bool IsVarArg = CLI.IsVarArg;
162+
EVT PtrVT = getPointerTy(DAG.getDataLayout());
163+
164+
if (IsVarArg) {
165+
report_fatal_error("LowerCall with varargs not implemented");
166+
}
167+
168+
MachineFunction &MF = DAG.getMachineFunction();
169+
170+
// Analyze the operands of the call, assigning locations to each operand.
171+
SmallVector<CCValAssign, 16> ArgLocs;
172+
CCState ArgCCInfo(CallConv, IsVarArg, MF, ArgLocs, *DAG.getContext());
173+
ArgCCInfo.AnalyzeCallOperands(Outs, CC_RISCV32);
174+
175+
// Get a count of how many bytes are to be pushed on the stack.
176+
unsigned NumBytes = ArgCCInfo.getNextStackOffset();
177+
178+
for (auto &Arg : Outs) {
179+
if (!Arg.Flags.isByVal())
180+
continue;
181+
report_fatal_error("Passing arguments byval not yet implemented");
182+
}
183+
184+
Chain = DAG.getCALLSEQ_START(Chain, NumBytes, 0, CLI.DL);
185+
186+
// Copy argument values to their designated locations.
187+
SmallVector<std::pair<unsigned, SDValue>, 8> RegsToPass;
188+
SDValue StackPtr;
189+
for (unsigned I = 0, E = ArgLocs.size(); I != E; ++I) {
190+
CCValAssign &VA = ArgLocs[I];
191+
SDValue ArgValue = OutVals[I];
192+
193+
// Promote the value if needed.
194+
// For now, only handle fully promoted arguments.
195+
switch (VA.getLocInfo()) {
196+
case CCValAssign::Full:
197+
break;
198+
default:
199+
llvm_unreachable("Unknown loc info!");
200+
}
201+
202+
if (VA.isRegLoc()) {
203+
// Queue up the argument copies and emit them at the end.
204+
RegsToPass.push_back(std::make_pair(VA.getLocReg(), ArgValue));
205+
} else {
206+
assert(VA.isMemLoc() && "Argument not register or memory");
207+
report_fatal_error("Passing arguments via the stack not yet implemented");
208+
}
209+
}
210+
211+
SDValue Glue;
212+
213+
// Build a sequence of copy-to-reg nodes, chained and glued together.
214+
for (auto &Reg : RegsToPass) {
215+
Chain = DAG.getCopyToReg(Chain, DL, Reg.first, Reg.second, Glue);
216+
Glue = Chain.getValue(1);
217+
}
218+
219+
if (isa<GlobalAddressSDNode>(Callee)) {
220+
Callee = lowerGlobalAddress(Callee, DAG);
221+
} else if (isa<ExternalSymbolSDNode>(Callee)) {
222+
report_fatal_error(
223+
"lowerExternalSymbol, needed for lowerCall, not yet handled");
224+
}
225+
226+
// The first call operand is the chain and the second is the target address.
227+
SmallVector<SDValue, 8> Ops;
228+
Ops.push_back(Chain);
229+
Ops.push_back(Callee);
230+
231+
// Add argument registers to the end of the list so that they are
232+
// known live into the call.
233+
for (auto &Reg : RegsToPass)
234+
Ops.push_back(DAG.getRegister(Reg.first, Reg.second.getValueType()));
235+
236+
// Add a register mask operand representing the call-preserved registers.
237+
const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo();
238+
const uint32_t *Mask = TRI->getCallPreservedMask(MF, CallConv);
239+
assert(Mask && "Missing call preserved mask for calling convention");
240+
Ops.push_back(DAG.getRegisterMask(Mask));
241+
242+
// Glue the call to the argument copies, if any.
243+
if (Glue.getNode())
244+
Ops.push_back(Glue);
245+
246+
// Emit the call.
247+
SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue);
248+
Chain = DAG.getNode(RISCVISD::CALL, DL, NodeTys, Ops);
249+
Glue = Chain.getValue(1);
250+
251+
// Mark the end of the call, which is glued to the call itself.
252+
Chain = DAG.getCALLSEQ_END(Chain,
253+
DAG.getConstant(NumBytes, DL, PtrVT, true),
254+
DAG.getConstant(0, DL, PtrVT, true),
255+
Glue, DL);
256+
Glue = Chain.getValue(1);
257+
258+
// Assign locations to each value returned by this call.
259+
SmallVector<CCValAssign, 16> RVLocs;
260+
CCState RetCCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext());
261+
RetCCInfo.AnalyzeCallResult(Ins, RetCC_RISCV32);
262+
263+
// Copy all of the result registers out of their specified physreg.
264+
for (auto &VA : RVLocs) {
265+
// Copy the value out, gluing the copy to the end of the call sequence.
266+
SDValue RetValue = DAG.getCopyFromReg(Chain, DL, VA.getLocReg(),
267+
VA.getLocVT(), Glue);
268+
Chain = RetValue.getValue(1);
269+
Glue = RetValue.getValue(2);
270+
271+
InVals.push_back(Chain.getValue(0));
272+
}
273+
274+
return Chain;
275+
}
276+
147277
SDValue
148278
RISCVTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv,
149279
bool IsVarArg,
@@ -194,6 +324,8 @@ const char *RISCVTargetLowering::getTargetNodeName(unsigned Opcode) const {
194324
break;
195325
case RISCVISD::RET_FLAG:
196326
return "RISCVISD::RET_FLAG";
327+
case RISCVISD::CALL:
328+
return "RISCVISD::CALL";
197329
}
198330
return nullptr;
199331
}

lib/Target/RISCV/RISCVISelLowering.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ class RISCVSubtarget;
2424
namespace RISCVISD {
2525
enum NodeType : unsigned {
2626
FIRST_NUMBER = ISD::BUILTIN_OP_END,
27-
RET_FLAG
27+
RET_FLAG,
28+
CALL
2829
};
2930
}
3031

@@ -52,6 +53,8 @@ class RISCVTargetLowering : public TargetLowering {
5253
const SmallVectorImpl<ISD::OutputArg> &Outs,
5354
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
5455
SelectionDAG &DAG) const override;
56+
SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
57+
SmallVectorImpl<SDValue> &InVals) const override;
5558
bool shouldConvertConstantLoadToIntImm(const APInt &Imm,
5659
Type *Ty) const override {
5760
return true;

lib/Target/RISCV/RISCVInstrInfo.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828

2929
using namespace llvm;
3030

31-
RISCVInstrInfo::RISCVInstrInfo() : RISCVGenInstrInfo() {}
31+
RISCVInstrInfo::RISCVInstrInfo()
32+
: RISCVGenInstrInfo(RISCV::ADJCALLSTACKDOWN, RISCV::ADJCALLSTACKUP) {}
3233

3334
void RISCVInstrInfo::copyPhysReg(MachineBasicBlock &MBB,
3435
MachineBasicBlock::iterator MBBI,

lib/Target/RISCV/RISCVInstrInfo.td

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,22 @@ include "RISCVInstrFormats.td"
1717
// RISC-V specific DAG Nodes.
1818
//===----------------------------------------------------------------------===//
1919

20-
def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone,
21-
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
20+
def SDT_RISCVCall : SDTypeProfile<0, -1, [SDTCisVT<0, XLenVT>]>;
21+
def SDT_RISCVCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>,
22+
SDTCisVT<1, i32>]>;
23+
def SDT_RISCVCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>,
24+
SDTCisVT<1, i32>]>;
25+
26+
27+
def Call : SDNode<"RISCVISD::CALL", SDT_RISCVCall,
28+
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue,
29+
SDNPVariadic]>;
30+
def CallSeqStart : SDNode<"ISD::CALLSEQ_START", SDT_RISCVCallSeqStart,
31+
[SDNPHasChain, SDNPOutGlue]>;
32+
def CallSeqEnd : SDNode<"ISD::CALLSEQ_END", SDT_RISCVCallSeqEnd,
33+
[SDNPHasChain, SDNPOptInGlue, SDNPOutGlue]>;
34+
def RetFlag : SDNode<"RISCVISD::RET_FLAG", SDTNone,
35+
[SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>;
2236

2337
//===----------------------------------------------------------------------===//
2438
// Operand and SDNode transformation definitions.
@@ -340,6 +354,10 @@ let isBarrier = 1, isBranch = 1, isTerminator = 1 in
340354
def PseudoBR : Pseudo<(outs), (ins simm21_lsb0:$imm20), [(br bb:$imm20)]>,
341355
PseudoInstExpansion<(JAL X0, simm21_lsb0:$imm20)>;
342356

357+
let isCall = 1, Defs=[X1] in
358+
def PseudoCALL : Pseudo<(outs), (ins GPR:$rs1), [(Call GPR:$rs1)]>,
359+
PseudoInstExpansion<(JALR X1, GPR:$rs1, 0)>;
360+
343361
let isBarrier = 1, isReturn = 1, isTerminator = 1 in
344362
def PseudoRET : Pseudo<(outs), (ins), [(RetFlag)]>,
345363
PseudoInstExpansion<(JALR X0, X1, 0)>;
@@ -371,3 +389,13 @@ multiclass StPat<PatFrag StoreOp, RVInst Inst> {
371389
defm : StPat<truncstorei8, SB>;
372390
defm : StPat<truncstorei16, SH>;
373391
defm : StPat<store, SW>;
392+
393+
/// Other pseudo-instructions
394+
395+
// Pessimistically assume the stack pointer will be clobbered
396+
let Defs = [X2], Uses = [X2] in {
397+
def ADJCALLSTACKDOWN : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
398+
[(CallSeqStart timm:$amt1, timm:$amt2)]>;
399+
def ADJCALLSTACKUP : Pseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2),
400+
[(CallSeqEnd timm:$amt1, timm:$amt2)]>;
401+
} // Defs = [X2], Uses = [X2]

lib/Target/RISCV/RISCVMCInstLower.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ bool llvm::LowerRISCVMachineOperandToMCOperand(const MachineOperand &MO,
6868
return false;
6969
MCOp = MCOperand::createReg(MO.getReg());
7070
break;
71+
case MachineOperand::MO_RegisterMask:
72+
// Regmasks are like implicit defs.
73+
return false;
7174
case MachineOperand::MO_Immediate:
7275
MCOp = MCOperand::createImm(MO.getImm());
7376
break;

lib/Target/RISCV/RISCVRegisterInfo.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,3 +88,9 @@ void RISCVRegisterInfo::eliminateFrameIndex(MachineBasicBlock::iterator II,
8888
unsigned RISCVRegisterInfo::getFrameRegister(const MachineFunction &MF) const {
8989
return RISCV::X8;
9090
}
91+
92+
const uint32_t *
93+
RISCVRegisterInfo::getCallPreservedMask(const MachineFunction & /*MF*/,
94+
CallingConv::ID /*CC*/) const {
95+
return CSR_RegMask;
96+
}

lib/Target/RISCV/RISCVRegisterInfo.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ struct RISCVRegisterInfo : public RISCVGenRegisterInfo {
2525

2626
RISCVRegisterInfo(unsigned HwMode);
2727

28+
const uint32_t *getCallPreservedMask(const MachineFunction &MF,
29+
CallingConv::ID) const override;
30+
2831
const MCPhysReg *getCalleeSavedRegs(const MachineFunction *MF) const override;
2932

3033
BitVector getReservedRegs(const MachineFunction &MF) const override;

test/CodeGen/RISCV/calls.ll

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
2+
; RUN: llc -mtriple=riscv32 -verify-machineinstrs < %s \
3+
; RUN: | FileCheck -check-prefix=RV32I %s
4+
5+
declare i32 @external_function(i32)
6+
7+
define i32 @test_call_external(i32 %a) nounwind {
8+
; RV32I-LABEL: test_call_external:
9+
; RV32I: # BB#0:
10+
; RV32I-NEXT: sw ra, 12(s0)
11+
; RV32I-NEXT: lui a1, %hi(external_function)
12+
; RV32I-NEXT: addi a1, a1, %lo(external_function)
13+
; RV32I-NEXT: jalr ra, a1, 0
14+
; RV32I-NEXT: lw ra, 12(s0)
15+
; RV32I-NEXT: jalr zero, ra, 0
16+
%1 = call i32 @external_function(i32 %a)
17+
ret i32 %1
18+
}
19+
20+
define i32 @defined_function(i32 %a) nounwind {
21+
; RV32I-LABEL: defined_function:
22+
; RV32I: # BB#0:
23+
; RV32I-NEXT: addi a0, a0, 1
24+
; RV32I-NEXT: jalr zero, ra, 0
25+
%1 = add i32 %a, 1
26+
ret i32 %1
27+
}
28+
29+
define i32 @test_call_defined(i32 %a) nounwind {
30+
; RV32I-LABEL: test_call_defined:
31+
; RV32I: # BB#0:
32+
; RV32I-NEXT: sw ra, 12(s0)
33+
; RV32I-NEXT: lui a1, %hi(defined_function)
34+
; RV32I-NEXT: addi a1, a1, %lo(defined_function)
35+
; RV32I-NEXT: jalr ra, a1, 0
36+
; RV32I-NEXT: lw ra, 12(s0)
37+
; RV32I-NEXT: jalr zero, ra, 0
38+
%1 = call i32 @defined_function(i32 %a) nounwind
39+
ret i32 %1
40+
}
41+
42+
define i32 @test_call_indirect(i32 (i32)* %a, i32 %b) nounwind {
43+
; RV32I-LABEL: test_call_indirect:
44+
; RV32I: # BB#0:
45+
; RV32I-NEXT: sw ra, 12(s0)
46+
; RV32I-NEXT: addi a2, a0, 0
47+
; RV32I-NEXT: addi a0, a1, 0
48+
; RV32I-NEXT: jalr ra, a2, 0
49+
; RV32I-NEXT: lw ra, 12(s0)
50+
; RV32I-NEXT: jalr zero, ra, 0
51+
%1 = call i32 %a(i32 %b)
52+
ret i32 %1
53+
}
54+
55+
; Ensure that calls to fastcc functions aren't rejected. Such calls may be
56+
; introduced when compiling with optimisation.
57+
58+
define fastcc i32 @fastcc_function(i32 %a, i32 %b) nounwind {
59+
; RV32I-LABEL: fastcc_function:
60+
; RV32I: # BB#0:
61+
; RV32I-NEXT: add a0, a0, a1
62+
; RV32I-NEXT: jalr zero, ra, 0
63+
%1 = add i32 %a, %b
64+
ret i32 %1
65+
}
66+
67+
define i32 @test_call_fastcc(i32 %a, i32 %b) nounwind {
68+
; RV32I-LABEL: test_call_fastcc:
69+
; RV32I: # BB#0:
70+
; RV32I-NEXT: sw ra, 12(s0)
71+
; RV32I-NEXT: sw s1, 8(s0)
72+
; RV32I-NEXT: addi s1, a0, 0
73+
; RV32I-NEXT: lui a0, %hi(fastcc_function)
74+
; RV32I-NEXT: addi a2, a0, %lo(fastcc_function)
75+
; RV32I-NEXT: addi a0, s1, 0
76+
; RV32I-NEXT: jalr ra, a2, 0
77+
; RV32I-NEXT: addi a0, s1, 0
78+
; RV32I-NEXT: lw s1, 8(s0)
79+
; RV32I-NEXT: lw ra, 12(s0)
80+
; RV32I-NEXT: jalr zero, ra, 0
81+
%1 = call fastcc i32 @fastcc_function(i32 %a, i32 %b)
82+
ret i32 %a
83+
}

0 commit comments

Comments
 (0)