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 | |
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. |
29 | static 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. |
43 | static 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 | |
54 | bool 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 | |
62 | bool 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 | |
78 | bool 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. |
97 | bool 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. |
120 | bool 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. |
138 | bool 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. |
152 | bool 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. |
176 | bool 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. |
203 | bool 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 | |
230 | bool 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 | |
257 | WebAssemblyImportModuleAttr * |
258 | SemaWasm::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 | |
278 | WebAssemblyImportNameAttr * |
279 | SemaWasm::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 | |
298 | void 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 | |
315 | void 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 | |
331 | void 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 |
Definitions
- SemaWasm
- CheckWasmBuiltinArgIsTable
- CheckWasmBuiltinArgIsInteger
- BuiltinWasmRefNullExtern
- BuiltinWasmRefIsNullExtern
- BuiltinWasmRefNullFunc
- BuiltinWasmTableGet
- BuiltinWasmTableSet
- BuiltinWasmTableSize
- BuiltinWasmTableGrow
- BuiltinWasmTableFill
- BuiltinWasmTableCopy
- CheckWebAssemblyBuiltinFunctionCall
- mergeImportModuleAttr
- mergeImportNameAttr
- handleWebAssemblyImportModuleAttr
- handleWebAssemblyImportNameAttr
Learn to use CMake with our Intro Training
Find out more