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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang/lib/Sema/SemaWasm.cpp