1 //===------ SemaWasm.cpp ---- WebAssembly target-specific routines --------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file implements semantic analysis functions specific to WebAssembly.
11 //===----------------------------------------------------------------------===//
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"
25 SemaWasm::SemaWasm(Sema
&S
) : SemaBase(S
) {}
27 /// Checks the argument at the given index is a WebAssembly table and if it
28 /// is, sets ElTy to the element type.
29 static bool CheckWasmBuiltinArgIsTable(Sema
&S
, CallExpr
*E
, unsigned ArgIndex
,
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();
38 ElTy
= ATy
->getElementType();
42 /// Checks the argument at the given index is an integer.
43 static bool CheckWasmBuiltinArgIsInteger(Sema
&S
, CallExpr
*E
,
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();
54 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr
*TheCall
) {
55 if (TheCall
->getNumArgs() != 0)
58 TheCall
->setType(getASTContext().getWebAssemblyExternrefType());
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;
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
);
84 /// Check that the first argument is a WebAssembly table, and the second
85 /// is an index to use as index into the table.
86 bool SemaWasm::BuiltinWasmTableGet(CallExpr
*TheCall
) {
87 if (SemaRef
.checkArgCount(TheCall
, 2))
91 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, ElTy
))
94 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, 1))
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
);
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.
109 bool SemaWasm::BuiltinWasmTableSet(CallExpr
*TheCall
) {
110 if (SemaRef
.checkArgCount(TheCall
, 3))
114 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, ElTy
))
117 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, 1))
120 if (!getASTContext().hasSameType(ElTy
, TheCall
->getArg(2)->getType()))
126 /// Check that the argument is a WebAssembly table.
127 bool SemaWasm::BuiltinWasmTableSize(CallExpr
*TheCall
) {
128 if (SemaRef
.checkArgCount(TheCall
, 1))
132 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, ElTy
))
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.
141 bool SemaWasm::BuiltinWasmTableGrow(CallExpr
*TheCall
) {
142 if (SemaRef
.checkArgCount(TheCall
, 3))
146 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, ElTy
))
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();
156 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, 2))
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.
165 bool SemaWasm::BuiltinWasmTableFill(CallExpr
*TheCall
) {
166 if (SemaRef
.checkArgCount(TheCall
, 4))
170 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, ElTy
))
173 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, 1))
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();
183 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, 3))
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.
192 bool SemaWasm::BuiltinWasmTableCopy(CallExpr
*TheCall
) {
193 if (SemaRef
.checkArgCount(TheCall
, 5))
197 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 0, XElTy
))
201 if (CheckWasmBuiltinArgIsTable(SemaRef
, TheCall
, 1, YElTy
))
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();
211 for (int I
= 2; I
<= 4; I
++) {
212 if (CheckWasmBuiltinArgIsInteger(SemaRef
, TheCall
, I
))
219 bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo
&TI
,
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
);
244 WebAssemblyImportModuleAttr
*
245 SemaWasm::mergeImportModuleAttr(Decl
*D
,
246 const WebAssemblyImportModuleAttr
&AL
) {
247 auto *FD
= cast
<FunctionDecl
>(D
);
249 if (const auto *ExistingAttr
= FD
->getAttr
<WebAssemblyImportModuleAttr
>()) {
250 if (ExistingAttr
->getImportModule() == AL
.getImportModule())
252 Diag(ExistingAttr
->getLocation(), diag::warn_mismatched_import
)
253 << 0 << ExistingAttr
->getImportModule() << AL
.getImportModule();
254 Diag(AL
.getLoc(), diag::note_previous_attribute
);
258 Diag(AL
.getLoc(), diag::warn_import_on_definition
) << 0;
261 return ::new (getASTContext())
262 WebAssemblyImportModuleAttr(getASTContext(), AL
, AL
.getImportModule());
265 WebAssemblyImportNameAttr
*
266 SemaWasm::mergeImportNameAttr(Decl
*D
, const WebAssemblyImportNameAttr
&AL
) {
267 auto *FD
= cast
<FunctionDecl
>(D
);
269 if (const auto *ExistingAttr
= FD
->getAttr
<WebAssemblyImportNameAttr
>()) {
270 if (ExistingAttr
->getImportName() == AL
.getImportName())
272 Diag(ExistingAttr
->getLocation(), diag::warn_mismatched_import
)
273 << 1 << ExistingAttr
->getImportName() << AL
.getImportName();
274 Diag(AL
.getLoc(), diag::note_previous_attribute
);
278 Diag(AL
.getLoc(), diag::warn_import_on_definition
) << 1;
281 return ::new (getASTContext())
282 WebAssemblyImportNameAttr(getASTContext(), AL
, AL
.getImportName());
285 void SemaWasm::handleWebAssemblyImportModuleAttr(Decl
*D
,
286 const ParsedAttr
&AL
) {
287 auto *FD
= cast
<FunctionDecl
>(D
);
290 SourceLocation ArgLoc
;
291 if (!SemaRef
.checkStringLiteralArgumentAttr(AL
, 0, Str
, &ArgLoc
))
294 Diag(AL
.getLoc(), diag::warn_import_on_definition
) << 0;
298 FD
->addAttr(::new (getASTContext())
299 WebAssemblyImportModuleAttr(getASTContext(), AL
, Str
));
302 void SemaWasm::handleWebAssemblyImportNameAttr(Decl
*D
, const ParsedAttr
&AL
) {
303 auto *FD
= cast
<FunctionDecl
>(D
);
306 SourceLocation ArgLoc
;
307 if (!SemaRef
.checkStringLiteralArgumentAttr(AL
, 0, Str
, &ArgLoc
))
310 Diag(AL
.getLoc(), diag::warn_import_on_definition
) << 1;
314 FD
->addAttr(::new (getASTContext())
315 WebAssemblyImportNameAttr(getASTContext(), AL
, Str
));
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
;
326 auto *FD
= cast
<FunctionDecl
>(D
);
327 if (FD
->isThisDeclarationADefinition()) {
328 Diag(D
->getLocation(), diag::err_alias_is_definition
) << FD
<< 0;
333 SourceLocation ArgLoc
;
334 if (!SemaRef
.checkStringLiteralArgumentAttr(AL
, 0, Str
, &ArgLoc
))
337 D
->addAttr(::new (Context
) WebAssemblyExportNameAttr(Context
, AL
, Str
));
338 D
->addAttr(UsedAttr::CreateImplicit(Context
));