brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.4 KiB · e773113 Raw
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