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
23 namespace clang {
24
SemaWasm(Sema & S)25 SemaWasm::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.
CheckWasmBuiltinArgIsTable(Sema & S,CallExpr * E,unsigned ArgIndex,QualType & ElTy)29 static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex,
30 QualType &ElTy) {
31 Expr *ArgExpr = E->getArg(ArgIndex);
32 const auto *ATy = dyn_cast<ArrayType>(ArgExpr->getType());
33 if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) {
34 return S.Diag(ArgExpr->getBeginLoc(),
35 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.
CheckWasmBuiltinArgIsInteger(Sema & S,CallExpr * E,unsigned ArgIndex)43 static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,
44 unsigned ArgIndex) {
45 Expr *ArgExpr = E->getArg(ArgIndex);
46 if (!ArgExpr->getType()->isIntegerType()) {
47 return S.Diag(ArgExpr->getBeginLoc(),
48 diag::err_wasm_builtin_arg_must_be_integer_type)
49 << ArgIndex + 1 << ArgExpr->getSourceRange();
50 }
51 return false;
52 }
53
BuiltinWasmRefNullExtern(CallExpr * TheCall)54 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
55 if (TheCall->getNumArgs() != 0)
56 return true;
57
58 TheCall->setType(getASTContext().getWebAssemblyExternrefType());
59
60 return false;
61 }
62
BuiltinWasmRefNullFunc(CallExpr * TheCall)63 bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) {
64 ASTContext &Context = getASTContext();
65 if (TheCall->getNumArgs() != 0) {
66 Diag(TheCall->getBeginLoc(), 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(Context.VoidTy, {}, {});
75 QualType Type = Context.getPointerType(Pointee);
76 Pointee = Context.getAddrSpaceQualType(Pointee, LangAS::wasm_funcref);
77 Type = Context.getAttributedType(attr::WebAssemblyFuncref, Type,
78 Context.getPointerType(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.
BuiltinWasmTableGet(CallExpr * TheCall)86 bool SemaWasm::BuiltinWasmTableGet(CallExpr *TheCall) {
87 if (SemaRef.checkArgCount(TheCall, 2))
88 return true;
89
90 QualType ElTy;
91 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
92 return true;
93
94 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 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.
BuiltinWasmTableSet(CallExpr * TheCall)109 bool SemaWasm::BuiltinWasmTableSet(CallExpr *TheCall) {
110 if (SemaRef.checkArgCount(TheCall, 3))
111 return true;
112
113 QualType ElTy;
114 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
115 return true;
116
117 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
118 return true;
119
120 if (!getASTContext().hasSameType(ElTy, TheCall->getArg(2)->getType()))
121 return true;
122
123 return false;
124 }
125
126 /// Check that the argument is a WebAssembly table.
BuiltinWasmTableSize(CallExpr * TheCall)127 bool SemaWasm::BuiltinWasmTableSize(CallExpr *TheCall) {
128 if (SemaRef.checkArgCount(TheCall, 1))
129 return true;
130
131 QualType ElTy;
132 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 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.
BuiltinWasmTableGrow(CallExpr * TheCall)141 bool SemaWasm::BuiltinWasmTableGrow(CallExpr *TheCall) {
142 if (SemaRef.checkArgCount(TheCall, 3))
143 return true;
144
145 QualType ElTy;
146 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
147 return true;
148
149 Expr *NewElemArg = TheCall->getArg(1);
150 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {
151 return Diag(NewElemArg->getBeginLoc(),
152 diag::err_wasm_builtin_arg_must_match_table_element_type)
153 << 2 << 1 << NewElemArg->getSourceRange();
154 }
155
156 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 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.
BuiltinWasmTableFill(CallExpr * TheCall)165 bool SemaWasm::BuiltinWasmTableFill(CallExpr *TheCall) {
166 if (SemaRef.checkArgCount(TheCall, 4))
167 return true;
168
169 QualType ElTy;
170 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
171 return true;
172
173 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
174 return true;
175
176 Expr *NewElemArg = TheCall->getArg(2);
177 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {
178 return Diag(NewElemArg->getBeginLoc(),
179 diag::err_wasm_builtin_arg_must_match_table_element_type)
180 << 3 << 1 << NewElemArg->getSourceRange();
181 }
182
183 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 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.
BuiltinWasmTableCopy(CallExpr * TheCall)192 bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) {
193 if (SemaRef.checkArgCount(TheCall, 5))
194 return true;
195
196 QualType XElTy;
197 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy))
198 return true;
199
200 QualType YElTy;
201 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy))
202 return true;
203
204 Expr *TableYArg = TheCall->getArg(1);
205 if (!getASTContext().hasSameType(XElTy, YElTy)) {
206 return Diag(TableYArg->getBeginLoc(),
207 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(SemaRef, TheCall, I))
213 return true;
214 }
215
216 return false;
217 }
218
CheckWebAssemblyBuiltinFunctionCall(const TargetInfo & TI,unsigned BuiltinID,CallExpr * TheCall)219 bool 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
244 WebAssemblyImportModuleAttr *
mergeImportModuleAttr(Decl * D,const WebAssemblyImportModuleAttr & AL)245 SemaWasm::mergeImportModuleAttr(Decl *D,
246 const WebAssemblyImportModuleAttr &AL) {
247 auto *FD = cast<FunctionDecl>(D);
248
249 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) {
250 if (ExistingAttr->getImportModule() == AL.getImportModule())
251 return nullptr;
252 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
253 << 0 << ExistingAttr->getImportModule() << AL.getImportModule();
254 Diag(AL.getLoc(), diag::note_previous_attribute);
255 return nullptr;
256 }
257 if (FD->hasBody()) {
258 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
259 return nullptr;
260 }
261 return ::new (getASTContext())
262 WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule());
263 }
264
265 WebAssemblyImportNameAttr *
mergeImportNameAttr(Decl * D,const WebAssemblyImportNameAttr & AL)266 SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) {
267 auto *FD = cast<FunctionDecl>(D);
268
269 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) {
270 if (ExistingAttr->getImportName() == AL.getImportName())
271 return nullptr;
272 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
273 << 1 << ExistingAttr->getImportName() << AL.getImportName();
274 Diag(AL.getLoc(), diag::note_previous_attribute);
275 return nullptr;
276 }
277 if (FD->hasBody()) {
278 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
279 return nullptr;
280 }
281 return ::new (getASTContext())
282 WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName());
283 }
284
handleWebAssemblyImportModuleAttr(Decl * D,const ParsedAttr & AL)285 void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D,
286 const ParsedAttr &AL) {
287 auto *FD = cast<FunctionDecl>(D);
288
289 StringRef Str;
290 SourceLocation ArgLoc;
291 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
292 return;
293 if (FD->hasBody()) {
294 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
295 return;
296 }
297
298 FD->addAttr(::new (getASTContext())
299 WebAssemblyImportModuleAttr(getASTContext(), AL, Str));
300 }
301
handleWebAssemblyImportNameAttr(Decl * D,const ParsedAttr & AL)302 void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) {
303 auto *FD = cast<FunctionDecl>(D);
304
305 StringRef Str;
306 SourceLocation ArgLoc;
307 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
308 return;
309 if (FD->hasBody()) {
310 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
311 return;
312 }
313
314 FD->addAttr(::new (getASTContext())
315 WebAssemblyImportNameAttr(getASTContext(), AL, Str));
316 }
317
handleWebAssemblyExportNameAttr(Decl * D,const ParsedAttr & AL)318 void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) {
319 ASTContext &Context = getASTContext();
320 if (!isFuncOrMethodForAttrSubject(D)) {
321 Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
322 << AL << AL.isRegularKeywordAttribute() << ExpectedFunction;
323 return;
324 }
325
326 auto *FD = cast<FunctionDecl>(D);
327 if (FD->isThisDeclarationADefinition()) {
328 Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0;
329 return;
330 }
331
332 StringRef Str;
333 SourceLocation ArgLoc;
334 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
335 return;
336
337 D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str));
338 D->addAttr(UsedAttr::CreateImplicit(Context));
339 }
340
341 } // namespace clang
342