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 (TheCall->getNumArgs() != 0) 56 return true; 57 58 TheCall->setType(getASTContext().getWebAssemblyExternrefType()); 59 60 return false; 61 } 62 63 bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) { 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. 86 bool SemaWasm::BuiltinWasmTableGet(CallExpr *TheCall) { 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. 109 bool SemaWasm::BuiltinWasmTableSet(CallExpr *TheCall) { 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. 127 bool SemaWasm::BuiltinWasmTableSize(CallExpr *TheCall) { 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. 141 bool SemaWasm::BuiltinWasmTableGrow(CallExpr *TheCall) { 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. 165 bool SemaWasm::BuiltinWasmTableFill(CallExpr *TheCall) { 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. 192 bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) { 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 219 bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, 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 * 245 SemaWasm::mergeImportModuleAttr(Decl *D, 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 285 void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D, 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 302 void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) { 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 318 void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) { 319 ASTContext &Context = getASTContext(); 320 if (!isFuncOrMethodForAttrSubject(D)) { 321 Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type) 322 << AL << AL.isRegularKeywordAttribute() << ExpectedFunction; 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 342