xref: /freebsd/contrib/llvm-project/clang/lib/Sema/SemaWasm.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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(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.
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 
54 bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {
55   if (SemaRef.checkArgCount(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(TheCall, 1)) {
64     return true;
65   }
66 
67   Expr *ArgExpr = TheCall->getArg(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(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(Context.VoidTy, {}, {});
86   QualType Type = Context.getPointerType(Pointee);
87   Pointee = Context.getAddrSpaceQualType(Pointee, 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(TheCall, 2))
99     return true;
100 
101   QualType ElTy;
102   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
103     return true;
104 
105   if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 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(TheCall, 3))
122     return true;
123 
124   QualType ElTy;
125   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
126     return true;
127 
128   if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
129     return true;
130 
131   if (!getASTContext().hasSameType(ElTy, TheCall->getArg(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(TheCall, 1))
140     return true;
141 
142   QualType ElTy;
143   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 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(TheCall, 3))
154     return true;
155 
156   QualType ElTy;
157   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
158     return true;
159 
160   Expr *NewElemArg = TheCall->getArg(1);
161   if (!getASTContext().hasSameType(ElTy, 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(SemaRef, TheCall, 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(TheCall, 4))
178     return true;
179 
180   QualType ElTy;
181   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
182     return true;
183 
184   if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
185     return true;
186 
187   Expr *NewElemArg = TheCall->getArg(2);
188   if (!getASTContext().hasSameType(ElTy, 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(SemaRef, TheCall, 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(TheCall, 5))
205     return true;
206 
207   QualType XElTy;
208   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy))
209     return true;
210 
211   QualType YElTy;
212   if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy))
213     return true;
214 
215   Expr *TableYArg = TheCall->getArg(1);
216   if (!getASTContext().hasSameType(XElTy, 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(SemaRef, TheCall, 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>(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>(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>(D);
301 
302   StringRef Str;
303   SourceLocation ArgLoc;
304   if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &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>(D);
317 
318   StringRef Str;
319   SourceLocation ArgLoc;
320   if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &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>(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(AL, 0, Str, &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