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