clang  20.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"
14 #include "clang/AST/ASTContext.h"
15 #include "clang/AST/Decl.h"
16 #include "clang/AST/Type.h"
20 #include "clang/Sema/Attr.h"
21 #include "clang/Sema/Sema.h"
22 
23 namespace clang {
24 
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.
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 
55  if (TheCall->getNumArgs() != 0)
56  return true;
57 
58  TheCall->setType(getASTContext().getWebAssemblyExternrefType());
59 
60  return false;
61 }
62 
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;
69  return true;
70  }
71 
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);
80 
81  return false;
82 }
83 
84 /// Check that the first argument is a WebAssembly table, and the second
85 /// is an index to use as index into the table.
87  if (SemaRef.checkArgCount(TheCall, 2))
88  return true;
89 
90  QualType ElTy;
91  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
92  return true;
93 
94  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
95  return true;
96 
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);
102 
103  return false;
104 }
105 
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.
110  if (SemaRef.checkArgCount(TheCall, 3))
111  return true;
112 
113  QualType ElTy;
114  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
115  return true;
116 
117  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
118  return true;
119 
120  if (!getASTContext().hasSameType(ElTy, TheCall->getArg(2)->getType()))
121  return true;
122 
123  return false;
124 }
125 
126 /// Check that the argument is a WebAssembly table.
128  if (SemaRef.checkArgCount(TheCall, 1))
129  return true;
130 
131  QualType ElTy;
132  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
133  return true;
134 
135  return false;
136 }
137 
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.
142  if (SemaRef.checkArgCount(TheCall, 3))
143  return true;
144 
145  QualType ElTy;
146  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
147  return true;
148 
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();
154  }
155 
156  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 2))
157  return true;
158 
159  return false;
160 }
161 
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.
166  if (SemaRef.checkArgCount(TheCall, 4))
167  return true;
168 
169  QualType ElTy;
170  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))
171  return true;
172 
173  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))
174  return true;
175 
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();
181  }
182 
183  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 3))
184  return true;
185 
186  return false;
187 }
188 
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.
193  if (SemaRef.checkArgCount(TheCall, 5))
194  return true;
195 
196  QualType XElTy;
197  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy))
198  return true;
199 
200  QualType YElTy;
201  if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy))
202  return true;
203 
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();
209  }
210 
211  for (int I = 2; I <= 4; I++) {
212  if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, I))
213  return true;
214  }
215 
216  return false;
217 }
218 
220  unsigned BuiltinID,
221  CallExpr *TheCall) {
222  switch (BuiltinID) {
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);
239  }
240 
241  return false;
242 }
243 
244 WebAssemblyImportModuleAttr *
246  const WebAssemblyImportModuleAttr &AL) {
247  auto *FD = cast<FunctionDecl>(D);
248 
249  if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) {
250  if (ExistingAttr->getImportModule() == AL.getImportModule())
251  return nullptr;
252  Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
253  << 0 << ExistingAttr->getImportModule() << AL.getImportModule();
254  Diag(AL.getLoc(), diag::note_previous_attribute);
255  return nullptr;
256  }
257  if (FD->hasBody()) {
258  Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
259  return nullptr;
260  }
261  return ::new (getASTContext())
262  WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule());
263 }
264 
265 WebAssemblyImportNameAttr *
266 SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) {
267  auto *FD = cast<FunctionDecl>(D);
268 
269  if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) {
270  if (ExistingAttr->getImportName() == AL.getImportName())
271  return nullptr;
272  Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)
273  << 1 << ExistingAttr->getImportName() << AL.getImportName();
274  Diag(AL.getLoc(), diag::note_previous_attribute);
275  return nullptr;
276  }
277  if (FD->hasBody()) {
278  Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
279  return nullptr;
280  }
281  return ::new (getASTContext())
282  WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName());
283 }
284 
286  const ParsedAttr &AL) {
287  auto *FD = cast<FunctionDecl>(D);
288 
289  StringRef Str;
290  SourceLocation ArgLoc;
291  if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
292  return;
293  if (FD->hasBody()) {
294  Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;
295  return;
296  }
297 
298  FD->addAttr(::new (getASTContext())
299  WebAssemblyImportModuleAttr(getASTContext(), AL, Str));
300 }
301 
303  auto *FD = cast<FunctionDecl>(D);
304 
305  StringRef Str;
306  SourceLocation ArgLoc;
307  if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
308  return;
309  if (FD->hasBody()) {
310  Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;
311  return;
312  }
313 
314  FD->addAttr(::new (getASTContext())
315  WebAssemblyImportNameAttr(getASTContext(), AL, Str));
316 }
317 
319  ASTContext &Context = getASTContext();
321  Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)
323  return;
324  }
325 
326  auto *FD = cast<FunctionDecl>(D);
327  if (FD->isThisDeclarationADefinition()) {
328  Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0;
329  return;
330  }
331 
332  StringRef Str;
333  SourceLocation ArgLoc;
334  if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))
335  return;
336 
337  D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str));
338  D->addAttr(UsedAttr::CreateImplicit(Context));
339 }
340 
341 } // 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:187
SourceLocation getLoc() const
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2882
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.cpp:1693
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
Definition: Expr.h:3060
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition: Expr.h:3073
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
This represents one expression.
Definition: Expr.h:110
void setType(QualType t)
Definition: Expr.h:143
QualType getType() const
Definition: Expr.h:142
ParsedAttr - Represents a syntactic attribute.
Definition: ParsedAttr.h:129
A (possibly-)qualified type.
Definition: Type.h:941
SemaDiagnosticBuilder Diag(SourceLocation Loc, unsigned DiagID, bool DeferHint=false)
Emit a diagnostic.
Definition: SemaBase.cpp:64
ASTContext & getASTContext() const
Definition: SemaBase.cpp:9
Sema & SemaRef
Definition: SemaBase.h:40
void handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:302
bool BuiltinWasmRefNullExtern(CallExpr *TheCall)
Definition: SemaWasm.cpp:54
bool CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, unsigned BuiltinID, CallExpr *TheCall)
Definition: SemaWasm.cpp:219
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:86
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:165
bool BuiltinWasmTableSize(CallExpr *TheCall)
Check that the argument is a WebAssembly table.
Definition: SemaWasm.cpp:127
void handleWebAssemblyImportModuleAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:285
bool BuiltinWasmRefNullFunc(CallExpr *TheCall)
Definition: SemaWasm.cpp:63
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:109
SemaWasm(Sema &S)
Definition: SemaWasm.cpp:25
void handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL)
Definition: SemaWasm.cpp:318
WebAssemblyImportNameAttr * mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL)
Definition: SemaWasm.cpp:266
WebAssemblyImportModuleAttr * mergeImportModuleAttr(Decl *D, const WebAssemblyImportModuleAttr &AL)
Definition: SemaWasm.cpp:245
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:141
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:192
Sema - This implements semantic analysis and AST building for C.
Definition: Sema.h:493
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:326
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:338
Exposes information about the current target.
Definition: TargetInfo.h:218
The base class of the type hierarchy.
Definition: Type.h:1829
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: Type.h:8387
The JSON file list parser is used to communicate input to InstallAPI.
@ ExpectedFunction
Definition: ParsedAttr.h:1096
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:29
static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E, unsigned ArgIndex)
Checks the argument at the given index is an integer.
Definition: SemaWasm.cpp:43
bool isFuncOrMethodForAttrSubject(const Decl *D)
isFuncOrMethodForAttrSubject - Return true if the given decl has function type (function or function-...
Definition: Attr.h:34