clang 22.0.0git
SemaWasm.cpp
Go to the documentation of this file.
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"
15#include "clang/AST/Decl.h"
16#include "clang/AST/Type.h"
21#include "clang/Sema/Attr.h"
22#include "clang/Sema/Sema.h"
23
24namespace clang {
25
27
28/// Checks the argument at the given index is a WebAssembly table and if it
29/// is, sets ElTy to the element type.
30static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex,
31 QualType &ElTy) {
32 Expr *ArgExpr = E->getArg(ArgIndex);
33 const auto *ATy = dyn_cast<ArrayType>(ArgExpr->getType());
34 if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) {
35 return S.Diag(ArgExpr->getBeginLoc(),
36 diag::err_wasm_builtin_arg_must_be_table_type)
37 << ArgIndex + 1 << ArgExpr->getSourceRange();
38 }
39 ElTy = ATy->getElementType();
40 return false;
41}
42
43/// Checks the argument at the given index is an integer.
45 unsigned ArgIndex) {
46 Expr *ArgExpr = E->getArg(ArgIndex);
47 if (!ArgExpr->getType()->isIntegerType()) {
48 return S.Diag(ArgExpr->getBeginLoc(),
49 diag::err_wasm_builtin_arg_must_be_integer_type)
50 << ArgIndex + 1 << ArgExpr->getSourceRange();
51 }
52 return false;
53}
54
56 if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))
57 return true;
58 TheCall->setType(getASTContext().getWebAssemblyExternrefType());
59
60 return false;
61}
62
64 if (SemaRef.checkArgCount(TheCall, 1)) {
65 return true;
66 }
67
68 Expr *ArgExpr = TheCall->getArg(0);
69 if (!ArgExpr->getType().isWebAssemblyExternrefType()) {
70 SemaRef.Diag(ArgExpr->getBeginLoc(),
71 diag::err_wasm_builtin_arg_must_be_externref_type)
72 << 1 << ArgExpr->getSourceRange();
73 return true;
74 }
75
76 return false;
77}
78
80 ASTContext &Context = getASTContext();
81 if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))
82 return true;
83
84 // This custom type checking code ensures that the nodes are as expected
85 // in order to later on generate the necessary builtin.
86 QualType Pointee = Context.getFunctionType(Context.VoidTy, {}, {});
87 QualType Type = Context.getPointerType(Pointee);
88 Pointee = Context.getAddrSpaceQualType(Pointee, LangAS::wasm_funcref);
89 Type = Context.getAttributedType(attr::WebAssemblyFuncref, Type,
90 Context.getPointerType(Pointee));
91 TheCall->setType(Type);
92
93 return false;
94}
95
96/// Check that the first argument is a WebAssembly table, and the second
97/// is an index to use as index into the table.
99 if (SemaRef.checkArgCount(TheCall, 2))
100 return true;
101
102 QualType ElTy;
103 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
104 return true;
105
106 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
107 return true;
108
109 // If all is well, we set the type of TheCall to be the type of the
110 // element of the table.
111 // i.e. a table.get on an externref table has type externref,
112 // or whatever the type of the table element is.
113 TheCall->setType(ElTy);
114
115 return false;
116}
117
118/// Check that the first argumnet is a WebAssembly table, the second is
119/// an index to use as index into the table and the third is the reference
120/// type to set into the table.
122 if (SemaRef.checkArgCount(TheCall, 3))
123 return true;
124
125 QualType ElTy;
126 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
127 return true;
128
129 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
130 return true;
131
132 if (!getASTContext().hasSameType(ElTy, TheCall->getArg(2)->getType()))
133 return true;
134
135 return false;
136}
137
138/// Check that the argument is a WebAssembly table.
140 if (SemaRef.checkArgCount(TheCall, 1))
141 return true;
142
143 QualType ElTy;
144 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
145 return true;
146
147 return false;
148}
149
150/// Check that the first argument is a WebAssembly table, the second is the
151/// value to use for new elements (of a type matching the table type), the
152/// third value is an integer.
154 if (SemaRef.checkArgCount(TheCall, 3))
155 return true;
156
157 QualType ElTy;
158 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
159 return true;
160
161 Expr *NewElemArg = TheCall->getArg(1);
162 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {
163 return Diag(NewElemArg->getBeginLoc(),
164 diag::err_wasm_builtin_arg_must_match_table_element_type)
165 << 2 << 1 << NewElemArg->getSourceRange();
166 }
167
168 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 2))
169 return true;
170
171 return false;
172}
173
174/// Check that the first argument is a WebAssembly table, the second is an
175/// integer, the third is the value to use to fill the table (of a type
176/// matching the table type), and the fourth is an integer.
178 if (SemaRef.checkArgCount(TheCall, 4))
179 return true;
180
181 QualType ElTy;
182 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
183 return true;
184
185 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
186 return true;
187
188 Expr *NewElemArg = TheCall->getArg(2);
189 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {
190 return Diag(NewElemArg->getBeginLoc(),
191 diag::err_wasm_builtin_arg_must_match_table_element_type)
192 << 3 << 1 << NewElemArg->getSourceRange();
193 }
194
195 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 3))
196 return true;
197
198 return false;
199}
200
201/// Check that the first argument is a WebAssembly table, the second is also a
202/// WebAssembly table (of the same element type), and the third to fifth
203/// arguments are integers.
205 if (SemaRef.checkArgCount(TheCall, 5))
206 return true;
207
208 QualType XElTy;
209 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy))
210 return true;
211
212 QualType YElTy;
213 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy))
214 return true;
215
216 Expr *TableYArg = TheCall->getArg(1);
217 if (!getASTContext().hasSameType(XElTy, YElTy)) {
218 return Diag(TableYArg->getBeginLoc(),
219 diag::err_wasm_builtin_arg_must_match_table_element_type)
220 << 2 << 1 << TableYArg->getSourceRange();
221 }
222
223 for (int I = 2; I <= 4; I++) {
224 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, I))
225 return true;
226 }
227
228 return false;
229}
230
232 CallExpr *TheCall) {
233 if (SemaRef.checkArgCount(TheCall, 1))
234 return true;
235
236 Expr *FuncPtrArg = TheCall->getArg(0);
237 QualType ArgType = FuncPtrArg->getType();
238
239 // Check that the argument is a function pointer
240 const PointerType *PtrTy = ArgType->getAs<PointerType>();
241 if (!PtrTy) {
242 return Diag(FuncPtrArg->getBeginLoc(),
243 diag::err_typecheck_expect_function_pointer)
244 << ArgType << FuncPtrArg->getSourceRange();
245 }
246
247 const FunctionProtoType *FuncTy =
249 if (!FuncTy) {
250 return Diag(FuncPtrArg->getBeginLoc(),
251 diag::err_typecheck_expect_function_pointer)
252 << ArgType << FuncPtrArg->getSourceRange();
253 }
254
255 if (TI.getABI() == "experimental-mv") {
256 auto isStructOrUnion = [](QualType T) {
257 return T->isUnionType() || T->isStructureType();
258 };
259 if (isStructOrUnion(FuncTy->getReturnType())) {
260 return Diag(
261 FuncPtrArg->getBeginLoc(),
262 diag::
263 err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union)
264 << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange();
265 }
266 auto NParams = FuncTy->getNumParams();
267 for (unsigned I = 0; I < NParams; I++) {
268 if (isStructOrUnion(FuncTy->getParamType(I))) {
269 return Diag(
270 FuncPtrArg->getBeginLoc(),
271 diag::
272 err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union)
273 << 1 << FuncPtrArg->getSourceRange();
274 }
275 }
276 }
277
278 // Set return type to int (the result of the test)
279 TheCall->setType(getASTContext().IntTy);
280 return false;
281}
282
284 unsigned BuiltinID,
285 CallExpr *TheCall) {
286 switch (BuiltinID) {
287 case WebAssembly::BI__builtin_wasm_ref_null_extern:
288 return BuiltinWasmRefNullExtern(TheCall);
289 case WebAssembly::BI__builtin_wasm_ref_null_func:
290 return BuiltinWasmRefNullFunc(TheCall);
291 case WebAssembly::BI__builtin_wasm_ref_is_null_extern:
292 return BuiltinWasmRefIsNullExtern(TheCall);
293 case WebAssembly::BI__builtin_wasm_table_get:
294 return BuiltinWasmTableGet(TheCall);
295 case WebAssembly::BI__builtin_wasm_table_set:
296 return BuiltinWasmTableSet(TheCall);
297 case WebAssembly::BI__builtin_wasm_table_size:
298 return BuiltinWasmTableSize(TheCall);
299 case WebAssembly::BI__builtin_wasm_table_grow:
300 return BuiltinWasmTableGrow(TheCall);
301 case WebAssembly::BI__builtin_wasm_table_fill:
302 return BuiltinWasmTableFill(TheCall);
303 case WebAssembly::BI__builtin_wasm_table_copy:
304 return BuiltinWasmTableCopy(TheCall);
305 case WebAssembly::BI__builtin_wasm_test_function_pointer_signature:
306 return BuiltinWasmTestFunctionPointerSignature(TI, TheCall);
307 }
308
309 return false;
310}
311
312WebAssemblyImportModuleAttr *
314 const WebAssemblyImportModuleAttr &AL) {
315 auto *FD = cast<FunctionDecl>(D);
316
317 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) {
318 if (ExistingAttr->getImportModule() == AL.getImportModule())
319 return nullptr;
320 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
321 << 0 << ExistingAttr->getImportModule() << AL.getImportModule();
322 Diag(AL.getLoc(), diag::note_previous_attribute);
323 return nullptr;
324 }
325 if (FD->hasBody()) {
326 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
327 return nullptr;
328 }
329 return ::new (getASTContext())
330 WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule());
331}
332
333WebAssemblyImportNameAttr *
334SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) {
335 auto *FD = cast<FunctionDecl>(D);
336
337 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) {
338 if (ExistingAttr->getImportName() == AL.getImportName())
339 return nullptr;
340 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
341 << 1 << ExistingAttr->getImportName() << AL.getImportName();
342 Diag(AL.getLoc(), diag::note_previous_attribute);
343 return nullptr;
344 }
345 if (FD->hasBody()) {
346 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
347 return nullptr;
348 }
349 return ::new (getASTContext())
350 WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName());
351}
352
354 const ParsedAttr &AL) {
355 auto *FD = cast<FunctionDecl>(D);
356
357 StringRef Str;
358 SourceLocation ArgLoc;
359 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
360 return;
361 if (FD->hasBody()) {
362 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
363 return;
364 }
365
366 FD->addAttr(::new (getASTContext())
367 WebAssemblyImportModuleAttr(getASTContext(), AL, Str));
368}
369
371 auto *FD = cast<FunctionDecl>(D);
372
373 StringRef Str;
374 SourceLocation ArgLoc;
375 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
376 return;
377 if (FD->hasBody()) {
378 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
379 return;
380 }
381
382 FD->addAttr(::new (getASTContext())
383 WebAssemblyImportNameAttr(getASTContext(), AL, Str));
384}
385
387 ASTContext &Context = getASTContext();
389 Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
391 return;
392 }
393
394 auto *FD = cast<FunctionDecl>(D);
395 if (FD->isThisDeclarationADefinition()) {
396 Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0;
397 return;
398 }
399
400 StringRef Str;
401 SourceLocation ArgLoc;
402 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
403 return;
404
405 D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str));
406 D->addAttr(UsedAttr::CreateImplicit(Context));
407}
408
409} // namespace clang
Defines the clang::ASTContext interface.
Provides definitions for the various language-specific address spaces.
const Decl * D
Expr * E
This file declares semantic analysis functions specific to Wasm.
Enumerates target-specific builtins in their own namespaces within namespace clang.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
SourceLocation getLoc() const
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2879
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3083
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
This represents one expression.
Definition: Expr.h:112
void setType(QualType t)
Definition: Expr.h:145
QualType getType() const
Definition: Expr.h:144
Represents a prototype with parameter type info, e.g.
Definition: TypeBase.h:5282
unsigned getNumParams() const
Definition: TypeBase.h:5560
QualType getParamType(unsigned i) const
Definition: TypeBase.h:5562
QualType getReturnType() const
Definition: TypeBase.h:4818
ParsedAttr - Represents a syntactic attribute.
Definition: ParsedAttr.h:119
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition: TypeBase.h:3346
QualType getPointeeType() const
Definition: TypeBase.h:3356
A (possibly-)qualified type.
Definition: TypeBase.h:937
bool isWebAssemblyExternrefType() const
Returns true if it is a WebAssembly Externref Type.
Definition: Type.cpp:2948
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:61
ASTContext & getASTContext() const
Definition: SemaBase.cpp:9
Sema & SemaRef
Definition: SemaBase.h:40
void handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:370
bool BuiltinWasmRefNullExtern(CallExpr *TheCall)
Definition: SemaWasm.cpp:55
bool CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall)
Definition: SemaWasm.cpp:283
bool BuiltinWasmTableGet(CallExpr *TheCall)
Check that the first argument is a WebAssembly table, and the second is an index to use as index into...
Definition: SemaWasm.cpp:98
bool BuiltinWasmTableFill(CallExpr *TheCall)
Check that the first argument is a WebAssembly table, the second is an integer, the third is the valu...
Definition: SemaWasm.cpp:177
bool BuiltinWasmTableSize(CallExpr *TheCall)
Check that the argument is a WebAssembly table.
Definition: SemaWasm.cpp:139
void handleWebAssemblyImportModuleAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:353
bool BuiltinWasmRefNullFunc(CallExpr *TheCall)
Definition: SemaWasm.cpp:79
bool BuiltinWasmTestFunctionPointerSignature(const TargetInfo &TI, CallExpr *TheCall)
Definition: SemaWasm.cpp:231
bool BuiltinWasmTableSet(CallExpr *TheCall)
Check that the first argumnet is a WebAssembly table, the second is an index to use as index into the...
Definition: SemaWasm.cpp:121
SemaWasm(Sema &S)
Definition: SemaWasm.cpp:26
bool BuiltinWasmRefIsNullExtern(CallExpr *TheCall)
Definition: SemaWasm.cpp:63
void handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:386
WebAssemblyImportNameAttr * mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL)
Definition: SemaWasm.cpp:334
WebAssemblyImportModuleAttr * mergeImportModuleAttr(Decl *D, const WebAssemblyImportModuleAttr &AL)
Definition: SemaWasm.cpp:313
bool BuiltinWasmTableGrow(CallExpr *TheCall)
Check that the first argument is a WebAssembly table, the second is the value to use for new elements...
Definition: SemaWasm.cpp:153
bool BuiltinWasmTableCopy(CallExpr *TheCall)
Check that the first argument is a WebAssembly table, the second is also a WebAssembly table (of the ...
Definition: SemaWasm.cpp:204
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:850
bool checkArgCount(CallExpr *Call, unsigned DesiredArgCount)
Checks that a call expression's argument count is the desired number.
bool checkStringLiteralArgumentAttr(const AttributeCommonInfo &CI, const Expr *E, StringRef &Str, SourceLocation *ArgLocation=nullptr)
Check if the argument E is a ASCII string literal.
Encodes a location in the source.
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition: Stmt.cpp:334
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:346
Exposes information about the current target.
Definition: TargetInfo.h:226
virtual StringRef getABI() const
Get the ABI currently in use.
Definition: TargetInfo.h:1357
The base class of the type hierarchy.
Definition: TypeBase.h:1833
bool isStructureType() const
Definition: Type.cpp:678
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: TypeBase.h:8980
const T * getAs() const
Member-template getAs<specific type>'.
Definition: TypeBase.h:9159
bool isUnionType() const
Definition: Type.cpp:718
Defines the clang::TargetInfo interface.
The JSON file list parser is used to communicate input to InstallAPI.
@ ExpectedFunction
Definition: ParsedAttr.h:1075
static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex, QualType &ElTy)
Checks the argument at the given index is a WebAssembly table and if it is, sets ElTy to the element ...
Definition: SemaWasm.cpp:30
static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E, unsigned ArgIndex)
Checks the argument at the given index is an integer.
Definition: SemaWasm.cpp:44
bool isFuncOrMethodForAttrSubject(const Decl *D)
isFuncOrMethodForAttrSubject - Return true if the given decl has function type (function or function-...
Definition: Attr.h:34
const FunctionProtoType * T