1//===-- WebAssemblyCallLowering.cpp - Call lowering for GlobalISel -*- C++ -*-//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8///
9/// \file
10/// This file implements the lowering of LLVM calls to machine code calls for
11/// GlobalISel.
12///
13//===----------------------------------------------------------------------===//
14
15#include "WebAssemblyCallLowering.h"
16#include "MCTargetDesc/WebAssemblyMCTargetDesc.h"
17#include "Utils/WasmAddressSpaces.h"
18#include "WebAssemblyISelLowering.h"
19#include "WebAssemblyMachineFunctionInfo.h"
20#include "WebAssemblyRegisterInfo.h"
21#include "WebAssemblySubtarget.h"
22#include "WebAssemblyUtilities.h"
23#include "llvm/CodeGen/Analysis.h"
24#include "llvm/CodeGen/FunctionLoweringInfo.h"
25#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
26#include "llvm/CodeGen/MachineInstrBuilder.h"
27#include "llvm/CodeGen/RegisterBankInfo.h"
28#include "llvm/IR/DataLayout.h"
29#include "llvm/IR/DebugLoc.h"
30#include "llvm/IR/Value.h"
31
32#define DEBUG_TYPE "wasm-call-lowering"
33
34using namespace llvm;
35
36// Test whether the given calling convention is supported.
37static bool callingConvSupported(CallingConv::ID CallConv) {
38 // We currently support the language-independent target-independent
39 // conventions. We don't yet have a way to annotate calls with properties like
40 // "cold", and we don't have any call-clobbered registers, so these are mostly
41 // all handled the same.
42 return CallConv == CallingConv::C || CallConv == CallingConv::Fast ||
43 CallConv == CallingConv::Cold ||
44 CallConv == CallingConv::PreserveMost ||
45 CallConv == CallingConv::PreserveAll ||
46 CallConv == CallingConv::CXX_FAST_TLS ||
47 CallConv == CallingConv::WASM_EmscriptenInvoke ||
48 CallConv == CallingConv::Swift;
49}
50
51WebAssemblyCallLowering::WebAssemblyCallLowering(
52 const WebAssemblyTargetLowering &TLI)
53 : CallLowering(&TLI) {}
54
55bool WebAssemblyCallLowering::canLowerReturn(MachineFunction &MF,
56 CallingConv::ID CallConv,
57 SmallVectorImpl<BaseArgInfo> &Outs,
58 bool IsVarArg) const {
59 return WebAssembly::canLowerReturn(ResultSize: Outs.size(),
60 Subtarget: &MF.getSubtarget<WebAssemblySubtarget>());
61}
62
63bool WebAssemblyCallLowering::lowerReturn(MachineIRBuilder &MIRBuilder,
64 const Value *Val,
65 ArrayRef<Register> VRegs,
66 FunctionLoweringInfo &FLI,
67 Register SwiftErrorVReg) const {
68 if (!Val)
69 return true; // allow only void returns for now
70
71 return false;
72}
73
74static unsigned getWASMArgumentOpcode(MVT ArgType) {
75 switch (ArgType.SimpleTy) {
76 case MVT::i32:
77 return WebAssembly::ARGUMENT_i32;
78 case MVT::i64:
79 return WebAssembly::ARGUMENT_i64;
80 case MVT::f32:
81 return WebAssembly::ARGUMENT_f32;
82 case MVT::f64:
83 return WebAssembly::ARGUMENT_f64;
84
85 case MVT::funcref:
86 return WebAssembly::ARGUMENT_funcref;
87 case MVT::externref:
88 return WebAssembly::ARGUMENT_externref;
89 case MVT::exnref:
90 return WebAssembly::ARGUMENT_exnref;
91
92 case MVT::v16i8:
93 return WebAssembly::ARGUMENT_v16i8;
94 case MVT::v8i16:
95 return WebAssembly::ARGUMENT_v8i16;
96 case MVT::v4i32:
97 return WebAssembly::ARGUMENT_v4i32;
98 case MVT::v2i64:
99 return WebAssembly::ARGUMENT_v2i64;
100 case MVT::v8f16:
101 return WebAssembly::ARGUMENT_v8f16;
102 case MVT::v4f32:
103 return WebAssembly::ARGUMENT_v4f32;
104 case MVT::v2f64:
105 return WebAssembly::ARGUMENT_v2f64;
106 default:
107 break;
108 }
109 llvm_unreachable("Found unexpected type for WASM argument");
110}
111
112static LLT getLLTForWasmMVT(MVT Ty, const DataLayout &DL) {
113 if (Ty == MVT::externref) {
114 return LLT::pointer(
115 AddressSpace: WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF,
116 SizeInBits: DL.getPointerSizeInBits(
117 AS: WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF));
118 }
119
120 if (Ty == MVT::funcref) {
121 return LLT::pointer(
122 AddressSpace: WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF,
123 SizeInBits: DL.getPointerSizeInBits(
124 AS: WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF));
125 }
126
127 return llvm::getLLTForMVT(Ty);
128}
129
130bool WebAssemblyCallLowering::lowerFormalArguments(
131 MachineIRBuilder &MIRBuilder, const Function &F,
132 ArrayRef<ArrayRef<Register>> VRegs, FunctionLoweringInfo &FLI) const {
133 MachineFunction &MF = MIRBuilder.getMF();
134 MachineRegisterInfo &MRI = MF.getRegInfo();
135 WebAssemblyFunctionInfo *MFI = MF.getInfo<WebAssemblyFunctionInfo>();
136 const DataLayout &DL = F.getDataLayout();
137 const WebAssemblyTargetLowering &TLI = *getTLI<WebAssemblyTargetLowering>();
138 const WebAssemblySubtarget &Subtarget =
139 MF.getSubtarget<WebAssemblySubtarget>();
140 const WebAssemblyRegisterInfo &TRI = *Subtarget.getRegisterInfo();
141 const WebAssemblyInstrInfo &TII = *Subtarget.getInstrInfo();
142 const RegisterBankInfo &RBI = *Subtarget.getRegBankInfo();
143
144 LLVMContext &Ctx = MIRBuilder.getContext();
145 const CallingConv::ID CallConv = F.getCallingConv();
146
147 if (!callingConvSupported(CallConv)) {
148 return false;
149 }
150
151 MF.getRegInfo().addLiveIn(Reg: WebAssembly::ARGUMENTS);
152 MF.front().addLiveIn(PhysReg: WebAssembly::ARGUMENTS);
153
154 SmallVector<ArgInfo, 8> SplitArgs;
155
156 if (!FLI.CanLowerReturn) {
157 insertSRetIncomingArgument(F, SplitArgs, DemoteReg&: FLI.DemoteRegister, MRI, DL);
158 }
159
160 unsigned ArgIdx = 0;
161 bool HasSwiftErrorArg = false;
162 bool HasSwiftSelfArg = false;
163 for (const Argument &Arg : F.args()) {
164 ArgInfo OrigArg{VRegs[ArgIdx], Arg.getType(), ArgIdx};
165 setArgFlags(Arg&: OrigArg, OpIdx: ArgIdx + AttributeList::FirstArgIndex, DL, FuncInfo: F);
166
167 HasSwiftSelfArg |= Arg.hasSwiftSelfAttr();
168 HasSwiftErrorArg |= Arg.hasSwiftErrorAttr();
169 if (Arg.hasInAllocaAttr()) {
170 return false;
171 }
172 if (Arg.hasNestAttr()) {
173 return false;
174 }
175 splitToValueTypes(OrigArgInfo: OrigArg, SplitArgs, DL, CallConv: F.getCallingConv());
176 ++ArgIdx;
177 }
178
179 unsigned FinalArgIdx = 0;
180 for (ArgInfo &Arg : SplitArgs) {
181 const EVT OrigVT = TLI.getValueType(DL, Ty: Arg.Ty);
182 const MVT NewVT = TLI.getRegisterTypeForCallingConv(Context&: Ctx, CC: CallConv, VT: OrigVT);
183 const LLT OrigLLT =
184 getLLTForType(Ty&: *OrigVT.getTypeForEVT(Context&: F.getContext()), DL);
185 const LLT NewLLT = getLLTForWasmMVT(Ty: NewVT, DL);
186
187 // If we need to split the type over multiple regs, check it's a scenario
188 // we currently support.
189 const unsigned NumParts =
190 TLI.getNumRegistersForCallingConv(Context&: Ctx, CC: CallConv, VT: OrigVT);
191
192 const ISD::ArgFlagsTy OrigFlags = Arg.Flags[0];
193 Arg.Flags.clear();
194
195 for (unsigned Part = 0; Part < NumParts; ++Part) {
196 ISD::ArgFlagsTy Flags = OrigFlags;
197 if (Part == 0) {
198 Flags.setSplit();
199 } else {
200 Flags.setOrigAlign(Align(1));
201 if (Part == NumParts - 1)
202 Flags.setSplitEnd();
203 }
204
205 Arg.Flags.push_back(Elt: Flags);
206 }
207
208 Arg.OrigRegs.assign(in_start: Arg.Regs.begin(), in_end: Arg.Regs.end());
209 if (NumParts != 1 || OrigLLT != NewLLT) {
210 // If we can't directly assign the register, we need one or more
211 // intermediate values.
212 Arg.Regs.resize(N: NumParts);
213
214 // For each split register, create and assign a vreg that will store
215 // the incoming component of the larger value. These will later be
216 // merged to form the final vreg.
217 for (unsigned Part = 0; Part < NumParts; ++Part) {
218 Arg.Regs[Part] = MRI.createGenericVirtualRegister(Ty: NewLLT);
219 }
220 }
221
222 for (unsigned Part = 0; Part < NumParts; ++Part) {
223 MachineInstrBuilder ArgInst =
224 MIRBuilder.buildInstr(Opcode: getWASMArgumentOpcode(ArgType: NewVT))
225 .addDef(RegNo: Arg.Regs[Part])
226 .addImm(Val: FinalArgIdx);
227
228 constrainOperandRegClass(MF, TRI, MRI, TII, RBI, InsertPt&: *ArgInst,
229 II: ArgInst->getDesc(), RegMO&: ArgInst->getOperand(i: 0), OpIdx: 0);
230 MFI->addParam(VT: NewVT);
231 ++FinalArgIdx;
232 }
233
234 if (OrigVT != NewVT) {
235 buildCopyFromRegs(B&: MIRBuilder, OrigRegs: Arg.OrigRegs, Regs: Arg.Regs, LLTy: OrigLLT, PartLLT: NewLLT,
236 Flags: Arg.Flags[0]);
237 }
238 }
239
240 // For swiftcc, emit additional swiftself and swifterror arguments
241 // if there aren't. These additional arguments are also added for callee
242 // signature They are necessary to match callee and caller signature for
243 // indirect call.
244 if (CallConv == CallingConv::Swift) {
245 const MVT PtrVT = TLI.getPointerTy(DL);
246
247 if (!HasSwiftSelfArg) {
248 MFI->addParam(VT: PtrVT);
249 }
250 if (!HasSwiftErrorArg) {
251 MFI->addParam(VT: PtrVT);
252 }
253 }
254
255 // Varargs are copied into a buffer allocated by the caller, and a pointer to
256 // the buffer is passed as an argument.
257 if (F.isVarArg()) {
258 const MVT PtrVT = TLI.getPointerTy(DL, AS: 0);
259 const LLT PtrLLT = LLT::pointer(AddressSpace: 0, SizeInBits: DL.getPointerSizeInBits(AS: 0));
260 Register VarargVreg = MF.getRegInfo().createGenericVirtualRegister(Ty: PtrLLT);
261
262 MFI->setVarargBufferVreg(VarargVreg);
263
264 MachineInstrBuilder ArgInst =
265 MIRBuilder.buildInstr(Opcode: getWASMArgumentOpcode(ArgType: PtrVT))
266 .addDef(RegNo: VarargVreg)
267 .addImm(Val: FinalArgIdx);
268
269 constrainOperandRegClass(MF, TRI, MRI, TII, RBI, InsertPt&: *ArgInst,
270 II: ArgInst->getDesc(), RegMO&: ArgInst->getOperand(i: 0), OpIdx: 0);
271
272 MFI->addParam(VT: PtrVT);
273 ++FinalArgIdx;
274 }
275
276 // Record the number and types of arguments and results.
277 SmallVector<MVT, 4> Params;
278 SmallVector<MVT, 4> Results;
279 computeSignatureVTs(Ty: MF.getFunction().getFunctionType(), TargetFunc: &MF.getFunction(),
280 ContextFunc: MF.getFunction(), TM: MF.getTarget(), Params, Results);
281 for (MVT VT : Results)
282 MFI->addResult(VT);
283
284 // TODO: Use signatures in WebAssemblyMachineFunctionInfo too and unify
285 // the param logic here with ComputeSignatureVTs
286 assert(MFI->getParams().size() == Params.size() &&
287 std::equal(MFI->getParams().begin(), MFI->getParams().end(),
288 Params.begin()));
289 return true;
290}
291
292bool WebAssemblyCallLowering::lowerCall(MachineIRBuilder &MIRBuilder,
293 CallLoweringInfo &Info) const {
294 return false;
295}
296