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