410 lines · cpp
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-exception6//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/Basic/TargetInfo.h"21#include "clang/Sema/Attr.h"22#include "clang/Sema/Sema.h"23 24namespace clang {25 26SemaWasm::SemaWasm(Sema &S) : SemaBase(S) {}27 28/// Checks the argument at the given index is a WebAssembly table and if it29/// is, sets ElTy to the element type.30static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex,31 QualType &ElTy) {32 Expr *ArgExpr = E->getArg(ArgIndex);33 const auto *ATy = dyn_cast<ArrayType>(ArgExpr->getType());34 if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) {35 return S.Diag(ArgExpr->getBeginLoc(),36 diag::err_wasm_builtin_arg_must_be_table_type)37 << ArgIndex + 1 << ArgExpr->getSourceRange();38 }39 ElTy = ATy->getElementType();40 return false;41}42 43/// Checks the argument at the given index is an integer.44static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E,45 unsigned ArgIndex) {46 Expr *ArgExpr = E->getArg(ArgIndex);47 if (!ArgExpr->getType()->isIntegerType()) {48 return S.Diag(ArgExpr->getBeginLoc(),49 diag::err_wasm_builtin_arg_must_be_integer_type)50 << ArgIndex + 1 << ArgExpr->getSourceRange();51 }52 return false;53}54 55bool SemaWasm::BuiltinWasmRefNullExtern(CallExpr *TheCall) {56 if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))57 return true;58 TheCall->setType(getASTContext().getWebAssemblyExternrefType());59 60 return false;61}62 63bool SemaWasm::BuiltinWasmRefIsNullExtern(CallExpr *TheCall) {64 if (SemaRef.checkArgCount(TheCall, 1)) {65 return true;66 }67 68 Expr *ArgExpr = TheCall->getArg(0);69 if (!ArgExpr->getType().isWebAssemblyExternrefType()) {70 SemaRef.Diag(ArgExpr->getBeginLoc(),71 diag::err_wasm_builtin_arg_must_be_externref_type)72 << 1 << ArgExpr->getSourceRange();73 return true;74 }75 76 return false;77}78 79bool SemaWasm::BuiltinWasmRefNullFunc(CallExpr *TheCall) {80 ASTContext &Context = getASTContext();81 if (SemaRef.checkArgCount(TheCall, /*DesiredArgCount=*/0))82 return true;83 84 // This custom type checking code ensures that the nodes are as expected85 // in order to later on generate the necessary builtin.86 QualType Pointee = Context.getFunctionType(Context.VoidTy, {}, {});87 QualType Type = Context.getPointerType(Pointee);88 Pointee = Context.getAddrSpaceQualType(Pointee, LangAS::wasm_funcref);89 Type = Context.getAttributedType(attr::WebAssemblyFuncref, Type,90 Context.getPointerType(Pointee));91 TheCall->setType(Type);92 93 return false;94}95 96/// Check that the first argument is a WebAssembly table, and the second97/// is an index to use as index into the table.98bool SemaWasm::BuiltinWasmTableGet(CallExpr *TheCall) {99 if (SemaRef.checkArgCount(TheCall, 2))100 return true;101 102 QualType ElTy;103 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))104 return true;105 106 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))107 return true;108 109 // If all is well, we set the type of TheCall to be the type of the110 // element of the table.111 // i.e. a table.get on an externref table has type externref,112 // or whatever the type of the table element is.113 TheCall->setType(ElTy);114 115 return false;116}117 118/// Check that the first argumnet is a WebAssembly table, the second is119/// an index to use as index into the table and the third is the reference120/// type to set into the table.121bool SemaWasm::BuiltinWasmTableSet(CallExpr *TheCall) {122 if (SemaRef.checkArgCount(TheCall, 3))123 return true;124 125 QualType ElTy;126 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))127 return true;128 129 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))130 return true;131 132 if (!getASTContext().hasSameType(ElTy, TheCall->getArg(2)->getType()))133 return true;134 135 return false;136}137 138/// Check that the argument is a WebAssembly table.139bool SemaWasm::BuiltinWasmTableSize(CallExpr *TheCall) {140 if (SemaRef.checkArgCount(TheCall, 1))141 return true;142 143 QualType ElTy;144 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))145 return true;146 147 return false;148}149 150/// Check that the first argument is a WebAssembly table, the second is the151/// value to use for new elements (of a type matching the table type), the152/// third value is an integer.153bool SemaWasm::BuiltinWasmTableGrow(CallExpr *TheCall) {154 if (SemaRef.checkArgCount(TheCall, 3))155 return true;156 157 QualType ElTy;158 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))159 return true;160 161 Expr *NewElemArg = TheCall->getArg(1);162 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {163 return Diag(NewElemArg->getBeginLoc(),164 diag::err_wasm_builtin_arg_must_match_table_element_type)165 << 2 << 1 << NewElemArg->getSourceRange();166 }167 168 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 2))169 return true;170 171 return false;172}173 174/// Check that the first argument is a WebAssembly table, the second is an175/// integer, the third is the value to use to fill the table (of a type176/// matching the table type), and the fourth is an integer.177bool SemaWasm::BuiltinWasmTableFill(CallExpr *TheCall) {178 if (SemaRef.checkArgCount(TheCall, 4))179 return true;180 181 QualType ElTy;182 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, ElTy))183 return true;184 185 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 1))186 return true;187 188 Expr *NewElemArg = TheCall->getArg(2);189 if (!getASTContext().hasSameType(ElTy, NewElemArg->getType())) {190 return Diag(NewElemArg->getBeginLoc(),191 diag::err_wasm_builtin_arg_must_match_table_element_type)192 << 3 << 1 << NewElemArg->getSourceRange();193 }194 195 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, 3))196 return true;197 198 return false;199}200 201/// Check that the first argument is a WebAssembly table, the second is also a202/// WebAssembly table (of the same element type), and the third to fifth203/// arguments are integers.204bool SemaWasm::BuiltinWasmTableCopy(CallExpr *TheCall) {205 if (SemaRef.checkArgCount(TheCall, 5))206 return true;207 208 QualType XElTy;209 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 0, XElTy))210 return true;211 212 QualType YElTy;213 if (CheckWasmBuiltinArgIsTable(SemaRef, TheCall, 1, YElTy))214 return true;215 216 Expr *TableYArg = TheCall->getArg(1);217 if (!getASTContext().hasSameType(XElTy, YElTy)) {218 return Diag(TableYArg->getBeginLoc(),219 diag::err_wasm_builtin_arg_must_match_table_element_type)220 << 2 << 1 << TableYArg->getSourceRange();221 }222 223 for (int I = 2; I <= 4; I++) {224 if (CheckWasmBuiltinArgIsInteger(SemaRef, TheCall, I))225 return true;226 }227 228 return false;229}230 231bool SemaWasm::BuiltinWasmTestFunctionPointerSignature(const TargetInfo &TI,232 CallExpr *TheCall) {233 if (SemaRef.checkArgCount(TheCall, 1))234 return true;235 236 Expr *FuncPtrArg = TheCall->getArg(0);237 QualType ArgType = FuncPtrArg->getType();238 239 // Check that the argument is a function pointer240 const PointerType *PtrTy = ArgType->getAs<PointerType>();241 if (!PtrTy) {242 return Diag(FuncPtrArg->getBeginLoc(),243 diag::err_typecheck_expect_function_pointer)244 << ArgType << FuncPtrArg->getSourceRange();245 }246 247 const FunctionProtoType *FuncTy =248 PtrTy->getPointeeType()->getAs<FunctionProtoType>();249 if (!FuncTy) {250 return Diag(FuncPtrArg->getBeginLoc(),251 diag::err_typecheck_expect_function_pointer)252 << ArgType << FuncPtrArg->getSourceRange();253 }254 255 if (TI.getABI() == "experimental-mv") {256 auto isStructOrUnion = [](QualType T) {257 return T->isUnionType() || T->isStructureType();258 };259 if (isStructOrUnion(FuncTy->getReturnType())) {260 return Diag(261 FuncPtrArg->getBeginLoc(),262 diag::263 err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union)264 << 0 << FuncTy->getReturnType() << FuncPtrArg->getSourceRange();265 }266 auto NParams = FuncTy->getNumParams();267 for (unsigned I = 0; I < NParams; I++) {268 if (isStructOrUnion(FuncTy->getParamType(I))) {269 return Diag(270 FuncPtrArg->getBeginLoc(),271 diag::272 err_wasm_builtin_test_fp_sig_cannot_include_struct_or_union)273 << 1 << FuncPtrArg->getSourceRange();274 }275 }276 }277 278 // Set return type to int (the result of the test)279 TheCall->setType(getASTContext().IntTy);280 return false;281}282 283bool SemaWasm::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI,284 unsigned BuiltinID,285 CallExpr *TheCall) {286 switch (BuiltinID) {287 case WebAssembly::BI__builtin_wasm_ref_null_extern:288 return BuiltinWasmRefNullExtern(TheCall);289 case WebAssembly::BI__builtin_wasm_ref_null_func:290 return BuiltinWasmRefNullFunc(TheCall);291 case WebAssembly::BI__builtin_wasm_ref_is_null_extern:292 return BuiltinWasmRefIsNullExtern(TheCall);293 case WebAssembly::BI__builtin_wasm_table_get:294 return BuiltinWasmTableGet(TheCall);295 case WebAssembly::BI__builtin_wasm_table_set:296 return BuiltinWasmTableSet(TheCall);297 case WebAssembly::BI__builtin_wasm_table_size:298 return BuiltinWasmTableSize(TheCall);299 case WebAssembly::BI__builtin_wasm_table_grow:300 return BuiltinWasmTableGrow(TheCall);301 case WebAssembly::BI__builtin_wasm_table_fill:302 return BuiltinWasmTableFill(TheCall);303 case WebAssembly::BI__builtin_wasm_table_copy:304 return BuiltinWasmTableCopy(TheCall);305 case WebAssembly::BI__builtin_wasm_test_function_pointer_signature:306 return BuiltinWasmTestFunctionPointerSignature(TI, TheCall);307 }308 309 return false;310}311 312WebAssemblyImportModuleAttr *313SemaWasm::mergeImportModuleAttr(Decl *D,314 const WebAssemblyImportModuleAttr &AL) {315 auto *FD = cast<FunctionDecl>(D);316 317 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportModuleAttr>()) {318 if (ExistingAttr->getImportModule() == AL.getImportModule())319 return nullptr;320 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)321 << 0 << ExistingAttr->getImportModule() << AL.getImportModule();322 Diag(AL.getLoc(), diag::note_previous_attribute);323 return nullptr;324 }325 if (FD->hasBody()) {326 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;327 return nullptr;328 }329 return ::new (getASTContext())330 WebAssemblyImportModuleAttr(getASTContext(), AL, AL.getImportModule());331}332 333WebAssemblyImportNameAttr *334SemaWasm::mergeImportNameAttr(Decl *D, const WebAssemblyImportNameAttr &AL) {335 auto *FD = cast<FunctionDecl>(D);336 337 if (const auto *ExistingAttr = FD->getAttr<WebAssemblyImportNameAttr>()) {338 if (ExistingAttr->getImportName() == AL.getImportName())339 return nullptr;340 Diag(ExistingAttr->getLocation(), diag::warn_mismatched_import)341 << 1 << ExistingAttr->getImportName() << AL.getImportName();342 Diag(AL.getLoc(), diag::note_previous_attribute);343 return nullptr;344 }345 if (FD->hasBody()) {346 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;347 return nullptr;348 }349 return ::new (getASTContext())350 WebAssemblyImportNameAttr(getASTContext(), AL, AL.getImportName());351}352 353void SemaWasm::handleWebAssemblyImportModuleAttr(Decl *D,354 const ParsedAttr &AL) {355 auto *FD = cast<FunctionDecl>(D);356 357 StringRef Str;358 SourceLocation ArgLoc;359 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))360 return;361 if (FD->hasBody()) {362 Diag(AL.getLoc(), diag::warn_import_on_definition) << 0;363 return;364 }365 366 FD->addAttr(::new (getASTContext())367 WebAssemblyImportModuleAttr(getASTContext(), AL, Str));368}369 370void SemaWasm::handleWebAssemblyImportNameAttr(Decl *D, const ParsedAttr &AL) {371 auto *FD = cast<FunctionDecl>(D);372 373 StringRef Str;374 SourceLocation ArgLoc;375 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))376 return;377 if (FD->hasBody()) {378 Diag(AL.getLoc(), diag::warn_import_on_definition) << 1;379 return;380 }381 382 FD->addAttr(::new (getASTContext())383 WebAssemblyImportNameAttr(getASTContext(), AL, Str));384}385 386void SemaWasm::handleWebAssemblyExportNameAttr(Decl *D, const ParsedAttr &AL) {387 ASTContext &Context = getASTContext();388 if (!isFuncOrMethodForAttrSubject(D)) {389 Diag(D->getLocation(), diag::warn_attribute_wrong_decl_type)390 << AL << AL.isRegularKeywordAttribute() << ExpectedFunction;391 return;392 }393 394 auto *FD = cast<FunctionDecl>(D);395 if (FD->isThisDeclarationADefinition()) {396 Diag(D->getLocation(), diag::err_alias_is_definition) << FD << 0;397 return;398 }399 400 StringRef Str;401 SourceLocation ArgLoc;402 if (!SemaRef.checkStringLiteralArgumentAttr(AL, 0, Str, &ArgLoc))403 return;404 405 D->addAttr(::new (Context) WebAssemblyExportNameAttr(Context, AL, Str));406 D->addAttr(UsedAttr::CreateImplicit(Context));407}408 409} // namespace clang410