1//===------ SemaWasm.cpp ---- WebAssembly target-specific routines --------===//
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// This file implements semantic analysis functions specific to WebAssembly.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Sema/SemaWasm.h"
14#include "clang/AST/ASTContext.h"
15#include "clang/AST/Decl.h"
16#include "clang/AST/Type.h"
17#include "clang/Basic/AddressSpaces.h"
18#include "clang/Basic/DiagnosticSema.h"
19#include "clang/Basic/TargetBuiltins.h"
20#include "clang/Sema/Attr.h"
21#include "clang/Sema/Sema.h"
22
23namespace clang {
24
25SemaWasm::SemaWasm(Sema &S) : SemaBase(S) {}
26
27/// Checks the argument at the given index is a WebAssembly table and if it
28/// is, sets ElTy to the element type.
29static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex,
30 QualType &ElTy) {
31 Expr *ArgExpr = E->getArg(Arg: ArgIndex);
32 const auto *ATy = dyn_cast<ArrayType>(Val: ArgExpr->getType());
33 if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) {
34 return S.Diag(Loc: ArgExpr->getBeginLoc(),
35 DiagID: diag::err_wasm_builtin_arg_must_be_table_type)
36 << ArgIndex + 1 << ArgExpr->getSourceRange();
37 }
38 ElTy = ATy->getElementType();
39 return false;
40}
41
42/// Checks the argument at the given index is an integer.
43static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,
44 unsigned ArgIndex) {
45 Expr *ArgExpr = E->getArg(Arg: ArgIndex);
46 if (!ArgExpr->getType()->isIntegerType()) {
47 return S.Diag(Loc: ArgExpr->getBeginLoc(),
48 DiagID: diag::err_wasm_builtin_arg_must_be_integer_type)
49 << ArgIndex + 1 << ArgExpr->getSourceRange();
50 }
51 return false;
52}
53
54bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
55 if (TheCall->getNumArgs() != 0)
56 return true;
57
58 TheCall->setType(getASTContext().getWebAssemblyExternrefType());
59
60 return false;
61}
62
63bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) {
64 ASTContext &Context = getASTContext();
65 if (TheCall->getNumArgs() != 0) {
66 Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_typecheck_call_too_many_args)
67 << 0 /*function call*/ << /*expected*/ 0 << TheCall->getNumArgs()
68 << /*is non object*/ 0;
69 return true;
70 }
71
72 // This custom type checking code ensures that the nodes are as expected
73 // in order to later on generate the necessary builtin.
74 QualType Pointee = Context.getFunctionType(ResultTy: Context.VoidTy, Args: {}, EPI: {});
75 QualType Type = Context.getPointerType(T: Pointee);
76 Pointee = Context.getAddrSpaceQualType(T: Pointee, AddressSpace: LangAS::wasm_funcref);
77 Type = Context.getAttributedType(attrKind: attr::WebAssemblyFuncref, modifiedType: Type,
78 equivalentType: Context.getPointerType(T: Pointee));
79 TheCall->setType(Type);
80
81 return false;
82}
83
84/// Check that the first argument is a WebAssembly table, and the second
85/// is an index to use as index into the table.
86bool SemaWasm::BuiltinWasmTableGet(CallExpr *TheCall) {
87 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2))
88 return true;
89
90 QualType ElTy;
91 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy))
92 return true;
93
94 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: 1))
95 return true;
96
97 // If all is well, we set the type of TheCall to be the type of the
98 // element of the table.
99 // i.e. a table.get on an externref table has type externref,
100 // or whatever the type of the table element is.
101 TheCall->setType(ElTy);
102
103 return false;
104}
105
106/// Check that the first argumnet is a WebAssembly table, the second is
107/// an index to use as index into the table and the third is the reference
108/// type to set into the table.
109bool SemaWasm::BuiltinWasmTableSet(CallExpr *TheCall) {
110 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3))
111 return true;
112
113 QualType ElTy;
114 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy))
115 return true;
116
117 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: 1))
118 return true;
119
120 if (!getASTContext().hasSameType(T1: ElTy, T2: TheCall->getArg(Arg: 2)->getType()))
121 return true;
122
123 return false;
124}
125
126/// Check that the argument is a WebAssembly table.
127bool SemaWasm::BuiltinWasmTableSize(CallExpr *TheCall) {
128 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1))
129 return true;
130
131 QualType ElTy;
132 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy))
133 return true;
134
135 return false;
136}
137
138/// Check that the first argument is a WebAssembly table, the second is the
139/// value to use for new elements (of a type matching the table type), the
140/// third value is an integer.
141bool SemaWasm::BuiltinWasmTableGrow(CallExpr *TheCall) {
142 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 3))
143 return true;
144
145 QualType ElTy;
146 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy))
147 return true;
148
149 Expr *NewElemArg = TheCall->getArg(Arg: 1);
150 if (!getASTContext().hasSameType(T1: ElTy, T2: NewElemArg->getType())) {
151 return Diag(Loc: NewElemArg->getBeginLoc(),
152 DiagID: diag::err_wasm_builtin_arg_must_match_table_element_type)
153 << 2 << 1 << NewElemArg->getSourceRange();
154 }
155
156 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: 2))
157 return true;
158
159 return false;
160}
161
162/// Check that the first argument is a WebAssembly table, the second is an
163/// integer, the third is the value to use to fill the table (of a type
164/// matching the table type), and the fourth is an integer.
165bool SemaWasm::BuiltinWasmTableFill(CallExpr *TheCall) {
166 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 4))
167 return true;
168
169 QualType ElTy;
170 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy))
171 return true;
172
173 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: 1))
174 return true;
175
176 Expr *NewElemArg = TheCall->getArg(Arg: 2);
177 if (!getASTContext().hasSameType(T1: ElTy, T2: NewElemArg->getType())) {
178 return Diag(Loc: NewElemArg->getBeginLoc(),
179 DiagID: diag::err_wasm_builtin_arg_must_match_table_element_type)
180 << 3 << 1 << NewElemArg->getSourceRange();
181 }
182
183 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: 3))
184 return true;
185
186 return false;
187}
188
189/// Check that the first argument is a WebAssembly table, the second is also a
190/// WebAssembly table (of the same element type), and the third to fifth
191/// arguments are integers.
192bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) {
193 if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 5))
194 return true;
195
196 QualType XElTy;
197 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 0, ElTy&: XElTy))
198 return true;
199
200 QualType YElTy;
201 if (CheckWasmBuiltinArgIsTable(S&: SemaRef, E: TheCall, ArgIndex: 1, ElTy&: YElTy))
202 return true;
203
204 Expr *TableYArg = TheCall->getArg(Arg: 1);
205 if (!getASTContext().hasSameType(T1: XElTy, T2: YElTy)) {
206 return Diag(Loc: TableYArg->getBeginLoc(),
207 DiagID: diag::err_wasm_builtin_arg_must_match_table_element_type)
208 << 2 << 1 << TableYArg->getSourceRange();
209 }
210
211 for (int I = 2; I <= 4; I++) {
212 if (CheckWasmBuiltinArgIsInteger(S&: SemaRef, E: TheCall, ArgIndex: I))
213 return true;
214 }
215
216 return false;
217}
218
219bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,
220 unsigned BuiltinID,
221 CallExpr *TheCall) {
222 switch (BuiltinID) {
223 case WebAssembly::BI__builtin_wasm_ref_null_extern:
224 return BuiltinWasmRefNullExtern(TheCall);
225 case WebAssembly::BI__builtin_wasm_ref_null_func:
226 return BuiltinWasmRefNullFunc(TheCall);
227 case WebAssembly::BI__builtin_wasm_table_get:
228 return BuiltinWasmTableGet(TheCall);
229 case WebAssembly::BI__builtin_wasm_table_set:
230 return BuiltinWasmTableSet(TheCall);
231 case WebAssembly::BI__builtin_wasm_table_size:
232 return BuiltinWasmTableSize(TheCall);
233 case WebAssembly::BI__builtin_wasm_table_grow:
234 return BuiltinWasmTableGrow(TheCall);
235 case WebAssembly::BI__builtin_wasm_table_fill:
236 return BuiltinWasmTableFill(TheCall);
237 case WebAssembly::BI__builtin_wasm_table_copy:
238 return BuiltinWasmTableCopy(TheCall);
239 }
240
241 return false;
242}
243
244WebAssemblyImportModuleAttr *
245SemaWasm::mergeImportModuleAttr(Decl *D,
246 const WebAssemblyImportModuleAttr &AL) {
247 auto *FD = cast<FunctionDecl>(Val: D);
248
249 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) {
250 if (ExistingAttr->getImportModule() == AL.getImportModule())
251 return nullptr;
252 Diag(Loc: ExistingAttr->getLocation(), DiagID: diag::warn_mismatched_import)
253 << 0 << ExistingAttr->getImportModule() << AL.getImportModule();
254 Diag(Loc: AL.getLoc(), DiagID: diag::note_previous_attribute);
255 return nullptr;
256 }
257 if (FD->hasBody()) {
258 Diag(Loc: AL.getLoc(), DiagID: diag::warn_import_on_definition) << 0;
259 return nullptr;
260 }
261 return ::new (getASTContext())
262 WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule());
263}
264
265WebAssemblyImportNameAttr *
266SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) {
267 auto *FD = cast<FunctionDecl>(Val: D);
268
269 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) {
270 if (ExistingAttr->getImportName() == AL.getImportName())
271 return nullptr;
272 Diag(Loc: ExistingAttr->getLocation(), DiagID: diag::warn_mismatched_import)
273 << 1 << ExistingAttr->getImportName() << AL.getImportName();
274 Diag(Loc: AL.getLoc(), DiagID: diag::note_previous_attribute);
275 return nullptr;
276 }
277 if (FD->hasBody()) {
278 Diag(Loc: AL.getLoc(), DiagID: diag::warn_import_on_definition) << 1;
279 return nullptr;
280 }
281 return ::new (getASTContext())
282 WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName());
283}
284
285void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D,
286 const ParsedAttr &AL) {
287 auto *FD = cast<FunctionDecl>(Val: D);
288
289 StringRef Str;
290 SourceLocation ArgLoc;
291 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str, ArgLocation: &ArgLoc))
292 return;
293 if (FD->hasBody()) {
294 Diag(Loc: AL.getLoc(), DiagID: diag::warn_import_on_definition) << 0;
295 return;
296 }
297
298 FD->addAttr(A: ::new (getASTContext())
299 WebAssemblyImportModuleAttr(getASTContext(), AL, Str));
300}
301
302void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) {
303 auto *FD = cast<FunctionDecl>(Val: D);
304
305 StringRef Str;
306 SourceLocation ArgLoc;
307 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str, ArgLocation: &ArgLoc))
308 return;
309 if (FD->hasBody()) {
310 Diag(Loc: AL.getLoc(), DiagID: diag::warn_import_on_definition) << 1;
311 return;
312 }
313
314 FD->addAttr(A: ::new (getASTContext())
315 WebAssemblyImportNameAttr(getASTContext(), AL, Str));
316}
317
318void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) {
319 ASTContext &Context = getASTContext();
320 if (!isFuncOrMethodForAttrSubject(D)) {
321 Diag(Loc: D->getLocation(), DiagID: diag::warn_attribute_wrong_decl_type)
322 << AL << AL.isRegularKeywordAttribute() << ExpectedFunction;
323 return;
324 }
325
326 auto *FD = cast<FunctionDecl>(Val: D);
327 if (FD->isThisDeclarationADefinition()) {
328 Diag(Loc: D->getLocation(), DiagID: diag::err_alias_is_definition) << FD << 0;
329 return;
330 }
331
332 StringRef Str;
333 SourceLocation ArgLoc;
334 if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str, ArgLocation: &ArgLoc))
335 return;
336
337 D->addAttr(A: ::new (Context) WebAssemblyExportNameAttr(Context, AL, Str));
338 D->addAttr(A: UsedAttr::CreateImplicit(Ctx&: Context));
339}
340
341} // namespace clang
342