1//===- WebAssembly.cpp ----------------------------------------------------===//
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#include "ABIInfoImpl.h"
10#include "TargetInfo.h"
11#include "clang/Basic/DiagnosticFrontend.h"
12
13using namespace clang;
14using namespace clang::CodeGen;
15
16//===----------------------------------------------------------------------===//
17// WebAssembly ABI Implementation
18//
19// This is a very simple ABI that relies a lot on DefaultABIInfo.
20//===----------------------------------------------------------------------===//
21
22class WebAssemblyABIInfo final : public ABIInfo {
23 DefaultABIInfo defaultInfo;
24 WebAssemblyABIKind Kind;
25
26public:
27 explicit WebAssemblyABIInfo(CodeGen::CodeGenTypes &CGT,
28 WebAssemblyABIKind Kind)
29 : ABIInfo(CGT), defaultInfo(CGT), Kind(Kind) {}
30
31private:
32 ABIArgInfo classifyReturnType(QualType RetTy) const;
33 ABIArgInfo classifyArgumentType(QualType Ty) const;
34
35 // DefaultABIInfo's classifyReturnType and classifyArgumentType are
36 // non-virtual, but computeInfo and EmitVAArg are virtual, so we
37 // overload them.
38 void computeInfo(CGFunctionInfo &FI) const override {
39 if (!getCXXABI().classifyReturnType(FI))
40 FI.getReturnInfo() = classifyReturnType(RetTy: FI.getReturnType());
41 for (auto &Arg : FI.arguments())
42 Arg.info = classifyArgumentType(Ty: Arg.type);
43 }
44
45 RValue EmitVAArg(CodeGenFunction &CGF, Address VAListAddr, QualType Ty,
46 AggValueSlot Slot) const override;
47};
48
49class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo {
50public:
51 explicit WebAssemblyTargetCodeGenInfo(CodeGen::CodeGenTypes &CGT,
52 WebAssemblyABIKind K)
53 : TargetCodeGenInfo(std::make_unique<WebAssemblyABIInfo>(args&: CGT, args&: K)) {
54 SwiftInfo =
55 std::make_unique<SwiftABIInfo>(args&: CGT, /*SwiftErrorInRegister=*/args: false);
56 }
57
58 void setTargetAttributes(const Decl *D, llvm::GlobalValue *GV,
59 CodeGen::CodeGenModule &CGM) const override {
60 TargetCodeGenInfo::setTargetAttributes(D, GV, M&: CGM);
61 if (const auto *VD = dyn_cast_or_null<VarDecl>(Val: D)) {
62 auto *Global = cast<llvm::GlobalVariable>(Val: GV);
63 const auto *ModuleAttr = VD->getAttr<WebAssemblyImportModuleAttr>();
64 const auto *NameAttr = VD->getAttr<WebAssemblyImportNameAttr>();
65 if (ModuleAttr || NameAttr) {
66 if (VD->isThisDeclarationADefinition() != VarDecl::DeclarationOnly) {
67 bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) ||
68 (NameAttr && !NameAttr->isInherited());
69 if (IsExplicit) {
70 auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation()
71 : NameAttr->getLocation();
72 CGM.getDiags().Report(Loc: AttrLoc, DiagID: diag::err_fe_backend_unsupported)
73 << "import attribute cannot be applied to a definition";
74 }
75 return;
76 }
77 if (Global->getAddressSpace() == 0) {
78 auto AttrLoc =
79 ModuleAttr ? ModuleAttr->getLocation() : NameAttr->getLocation();
80 CGM.getDiags().Report(Loc: AttrLoc, DiagID: diag::err_fe_backend_unsupported)
81 << "import attribute cannot be applied to a non-wasm-variable "
82 "global";
83 return;
84 }
85 if (ModuleAttr)
86 Global->addAttribute(Kind: "wasm-import-module",
87 Val: ModuleAttr->getImportModule());
88 if (NameAttr)
89 Global->addAttribute(Kind: "wasm-import-name", Val: NameAttr->getImportName());
90 }
91 if (const auto *Attr = VD->getAttr<WebAssemblyExportNameAttr>()) {
92 Global->addAttribute(Kind: "wasm-export-name", Val: Attr->getExportName());
93 }
94 } else if (const auto *FD = dyn_cast_or_null<FunctionDecl>(Val: D)) {
95 auto *Fn = cast<llvm::Function>(Val: GV);
96 const auto *ModuleAttr = FD->getAttr<WebAssemblyImportModuleAttr>();
97 const auto *NameAttr = FD->getAttr<WebAssemblyImportNameAttr>();
98 if (ModuleAttr || NameAttr) {
99 if (FD->isThisDeclarationADefinition()) {
100 bool IsExplicit = (ModuleAttr && !ModuleAttr->isInherited()) ||
101 (NameAttr && !NameAttr->isInherited());
102 if (IsExplicit) {
103 auto AttrLoc = ModuleAttr ? ModuleAttr->getLocation()
104 : NameAttr->getLocation();
105 CGM.getDiags().Report(Loc: AttrLoc, DiagID: diag::err_fe_backend_unsupported)
106 << "import attribute cannot be applied to a definition";
107 auto *NonConstFD = const_cast<FunctionDecl *>(FD);
108 NonConstFD->dropAttr<WebAssemblyImportModuleAttr>();
109 NonConstFD->dropAttr<WebAssemblyImportNameAttr>();
110 }
111 return;
112 }
113 if (ModuleAttr)
114 Fn->addFnAttr(Kind: "wasm-import-module", Val: ModuleAttr->getImportModule());
115 if (NameAttr)
116 Fn->addFnAttr(Kind: "wasm-import-name", Val: NameAttr->getImportName());
117 }
118 if (const auto *Attr = FD->getAttr<WebAssemblyExportNameAttr>()) {
119 Fn->addFnAttr(Kind: "wasm-export-name", Val: Attr->getExportName());
120 }
121
122 if (!FD->doesThisDeclarationHaveABody() && !FD->hasPrototype())
123 Fn->addFnAttr(Kind: "no-prototype");
124 }
125 }
126
127 /// Return the WebAssembly externref reference type.
128 virtual llvm::Type *getWasmExternrefReferenceType() const override {
129 return llvm::Type::getWasm_ExternrefTy(C&: getABIInfo().getVMContext());
130 }
131 /// Return the WebAssembly funcref reference type.
132 virtual llvm::Type *getWasmFuncrefReferenceType() const override {
133 return llvm::Type::getWasm_FuncrefTy(C&: getABIInfo().getVMContext());
134 }
135};
136
137/// Classify argument of given type \p Ty.
138ABIArgInfo WebAssemblyABIInfo::classifyArgumentType(QualType Ty) const {
139 Ty = useFirstFieldIfTransparentUnion(Ty);
140
141 if (isAggregateTypeForABI(T: Ty)) {
142 // Records with non-trivial destructors/copy-constructors should not be
143 // passed by value.
144 if (auto RAA = getRecordArgABI(T: Ty, CXXABI&: getCXXABI()))
145 return getNaturalAlignIndirect(Ty, AddrSpace: getDataLayout().getAllocaAddrSpace(),
146 ByVal: RAA == CGCXXABI::RAA_DirectInMemory);
147 // Ignore empty structs/unions.
148 if (isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true))
149 return ABIArgInfo::getIgnore();
150 // Lower single-element structs to just pass a regular value. TODO: We
151 // could do reasonable-size multiple-element structs too, using getExpand(),
152 // though watch out for things like bitfields.
153 if (const Type *SeltTy = isSingleElementStruct(T: Ty, Context&: getContext()))
154 return ABIArgInfo::getDirect(T: CGT.ConvertType(T: QualType(SeltTy, 0)));
155 // For the experimental multivalue ABI, fully expand all other aggregates
156 if (Kind == WebAssemblyABIKind::ExperimentalMV) {
157 if (Ty->getAs<ComplexType>())
158 return ABIArgInfo::getDirect();
159 const auto *RD = Ty->getAsRecordDecl();
160 if (RD) {
161 bool HasBitField = false;
162 for (auto *Field : RD->fields()) {
163 if (Field->isBitField()) {
164 HasBitField = true;
165 break;
166 }
167 }
168 if (!HasBitField)
169 return ABIArgInfo::getExpand();
170 }
171 }
172 }
173
174 // Otherwise just do the default thing.
175 return defaultInfo.classifyArgumentType(RetTy: Ty);
176}
177
178ABIArgInfo WebAssemblyABIInfo::classifyReturnType(QualType RetTy) const {
179 if (isAggregateTypeForABI(T: RetTy)) {
180 // Records with non-trivial destructors/copy-constructors should not be
181 // returned by value.
182 if (!getRecordArgABI(T: RetTy, CXXABI&: getCXXABI())) {
183 // Ignore empty structs/unions.
184 if (isEmptyRecord(Context&: getContext(), T: RetTy, AllowArrays: true))
185 return ABIArgInfo::getIgnore();
186 // Lower single-element structs to just return a regular value. TODO: We
187 // could do reasonable-size multiple-element structs too, using
188 // ABIArgInfo::getDirect().
189 if (const Type *SeltTy = isSingleElementStruct(T: RetTy, Context&: getContext()))
190 return ABIArgInfo::getDirect(T: CGT.ConvertType(T: QualType(SeltTy, 0)));
191 // For the experimental multivalue ABI, return all other aggregates
192 if (Kind == WebAssemblyABIKind::ExperimentalMV)
193 return ABIArgInfo::getDirect();
194 }
195 }
196
197 // Otherwise just do the default thing.
198 return defaultInfo.classifyReturnType(RetTy);
199}
200
201RValue WebAssemblyABIInfo::EmitVAArg(CodeGenFunction &CGF, Address VAListAddr,
202 QualType Ty, AggValueSlot Slot) const {
203 bool IsIndirect = isAggregateTypeForABI(T: Ty) &&
204 !isEmptyRecord(Context&: getContext(), T: Ty, AllowArrays: true) &&
205 !isSingleElementStruct(T: Ty, Context&: getContext());
206 return emitVoidPtrVAArg(CGF, VAListAddr, ValueTy: Ty, IsIndirect,
207 ValueInfo: getContext().getTypeInfoInChars(T: Ty),
208 SlotSizeAndAlign: CharUnits::fromQuantity(Quantity: 4),
209 /*AllowHigherAlign=*/true, Slot);
210}
211
212std::unique_ptr<TargetCodeGenInfo>
213CodeGen::createWebAssemblyTargetCodeGenInfo(CodeGenModule &CGM,
214 WebAssemblyABIKind K) {
215 return std::make_unique<WebAssemblyTargetCodeGenInfo>(args&: CGM.getTypes(), args&: K);
216}
217