diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -2238,6 +2238,128 @@ atomic_add(a, 1); } +WebAssembly Features +==================== + +Clang supports the WebAssembly features documented below. + +``__builtin_wasm_table_set`` +---------------------------- + +This builtin function stores a value in a WebAssembly table. +It takes three arguments. +The first argument is the table to store a value into, the second +argument is the index to which to store the value into, and the +third argument is a value of reference type to store in the table. +It returns nothing. + +.. code-block:: c++ + static __externref_t table[0]; + extern __externref_t JSObj; + + void store(int index) { + __builtin_wasm_table_set(table, index, JSObj); + } + +``__builtin_wasm_table_get`` +---------------------------- + +This builtin function is the counterpart to ``__builtin_wasm_table_set`` +and load a value from a WebAssembly table of reference typed values. +It takes 2 arguments. +The first argument is a table of reference typed values and the +second argument is an index from which to load the value. It returns +the loaded reference typed value. + +.. code-block:: c++ + static __externref_t table[0]; + + __externref_t load(int index) { + __externref_t Obj = __builtin_wasm_table_get(table, index); + return Obj; + } + +``__builtin_wasm_table_size`` +----------------------------- + +This builtin function returns the size of the WebAssembly table. +Takes as argument the table and returns an integer with the current +table size. + +.. code-block:: c++ + typedef void (*__funcref funcref_t)(); + static __funcref table[0]; + + int getSize() { + return __builtin_wasm_table_size(table); + } + +``__builtin_wasm_table_grow`` +----------------------------- + +This builtin function grows the WebAssembly table by a certain amount. +Currently, as all WebAssembly tables created in C/C++ are zero-sized, +this always needs to be called to grow the table. + +It takes three arguments. The first argument is the WebAssembly table +to grow. The second argument is the reference typed value to store in +the new table entries (the initialization value), and the third argument +is the amound to grow the table by. It returns the previous table size +or -1. It will return -1 if not enough space could be allocated. + +.. code-block:: c++ + typedef void (*__funcref funcref_t)(); + static __funcref table[0]; + + // grow returns the new table size or -1 on error. + int grow(__funcref fn, int delta) { + int prevSize = __builtin_wasm_table_grow(table, fn, delta); + if (prevSize == -1) + return -1; + return prevSize + delta; + } + +``__builtin_wasm_table_fill`` +----------------------------- + +This builtin function sets all the entries of a WebAssembly table to a given +reference typed value. It takes four arguments. The first argument is +the WebAssembly table, the second argument is the index that starts the +range, the third argument is the value to set in the new entries, and +the fourth and the last argument is the size of the range. It returns +nothing. + +.. code-block:: c++ + static __externref_t table[0]; + + // resets a table by setting all of its entries to a given value. + void reset(__externref_t Obj) { + int Size = __builtin_wasm_table_size(table); + __builtin_wasm_table_fill(table, 0, Obj, Size); + } + +``__builtin_wasm_table_copy`` +----------------------------- + +This builtin function copies elements from a source WebAssembly table +to a possibly overlapping destination region. It takes five arguments. +The first argument is the destination WebAssembly table, and the second +argument is the source WebAssembly table. The third argument is the +destination index from where the copy starts, the fourth argument is the +source index from there the copy starts, and the fifth and last argument +is the number of elements to copy. It returns nothing. + +.. code-block:: c++ + static __externref_t tableSrc[0]; + static __externref_t tableDst[0]; + + // Copy nelem elements from [src, src + nelem - 1] in tableSrc to + // [dst, dst + nelem - 1] in tableDst + void copy(int dst, int src, int nelem) { + __builtin_wasm_table_copy(tableDst, tableSrc, dst, src, nelem); + } + + Builtin Functions ================= diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -908,6 +908,15 @@ /// Returns true if it is not a class or if the class might not be dynamic. bool mayBeNotDynamicClass() const; + /// Returns true if it is a WebAssembly Reference Type. + bool isWebAssemblyReferenceType() const; + + /// Returns true if it is a WebAssembly Externref Type. + bool isWebAssemblyExternrefType() const; + + /// Returns true if it is a WebAssembly Funcref Type. + bool isWebAssemblyFuncrefType() const; + // Don't promise in the API that anything besides 'const' can be // easily added. @@ -2035,10 +2044,14 @@ /// Returns true for RVV scalable vector types. bool isRVVSizelessBuiltinType() const; - /// Check if this is a WebAssembly Reference Type. - bool isWebAssemblyReferenceType() const; + /// Check if this is a WebAssembly Externref Type. bool isWebAssemblyExternrefType() const; + /// Returns true if this is a WebAssembly table type: either an array of + /// reference types, or a pointer to a reference type (which can only be + /// created by array to pointer decay). + bool isWebAssemblyTableType() const; + /// Determines if this is a sizeless type supported by the /// 'arm_sve_vector_bits' type attribute, which can be applied to a single /// SVE vector or predicate, excluding tuple types such as svint32x4_t. diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -201,5 +201,13 @@ // return type. TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types") +// Table builtins +TARGET_BUILTIN(__builtin_wasm_table_set, "viii", "t", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_get, "iii", "t", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_size, "ii", "nt", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_grow, "iiii", "nt", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_fill, "viiii", "t", "reference-types") +TARGET_BUILTIN(__builtin_wasm_table_copy, "viiiii", "t", "reference-types") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -11812,4 +11812,34 @@ "cannot %select{capture|take address of}0 WebAssembly reference">; def err_wasm_funcref_not_wasm : Error< "invalid use of '__funcref' keyword outside the WebAssembly triple">; +def err_wasm_table_pr : Error< + "cannot form a %select{pointer|reference}0 to a WebAssembly table">; +def err_typecheck_wasm_table_must_have_zero_length : Error< + "only zero-length WebAssembly tables are currently supported">; +def err_wasm_table_in_function : Error< + "WebAssembly table cannot be declared within a function">; +def err_wasm_table_as_function_parameter : Error< + "cannot use WebAssembly table as a function parameter">; +def err_wasm_table_invalid_uett_operand : Error< + "invalid application of '%0' to WebAssembly table">; +def err_wasm_cast_table : Error< + "cannot cast %select{to|from}0 a WebAssembly table">; +def err_wasm_table_conditional_expression : Error< + "cannot use a WebAssembly table within a branch of a conditional expression">; +def err_wasm_table_art : Error< + "cannot %select{assign|return|throw|subscript}0 a WebAssembly table">; +def err_wasm_reftype_tc : Error< + "cannot %select{throw|catch}0 a WebAssembly reference type">; +def err_wasm_reftype_exception_spec : Error< + "WebAssembly reference type not allowed in exception specification">; +def err_wasm_table_must_be_static : Error< + "WebAssembly table must be static">; +def err_wasm_reftype_multidimensional_array : Error< + "multi-dimensional arrays of WebAssembly references are not allowed">; +def err_wasm_builtin_arg_must_be_table_type : Error < + "%ordinal0 argument must be a WebAssembly table">; +def err_wasm_builtin_arg_must_match_table_element_type : Error < + "%ordinal0 argument must match the element type of the WebAssembly table in the %ordinal1 argument">; +def err_wasm_builtin_arg_must_be_integer_type : Error < + "%ordinal0 argument must be an integer">; } // end of sema component. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13617,6 +13617,12 @@ // WebAssembly builtin handling. bool BuiltinWasmRefNullExtern(CallExpr *TheCall); bool BuiltinWasmRefNullFunc(CallExpr *TheCall); + bool BuiltinWasmTableGet(CallExpr *TheCall); + bool BuiltinWasmTableSet(CallExpr *TheCall); + bool BuiltinWasmTableSize(CallExpr *TheCall); + bool BuiltinWasmTableGrow(CallExpr *TheCall); + bool BuiltinWasmTableFill(CallExpr *TheCall); + bool BuiltinWasmTableCopy(CallExpr *TheCall); public: enum FormatStringType { diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -2347,16 +2347,22 @@ return false; } -bool Type::isWebAssemblyReferenceType() const { - return isWebAssemblyExternrefType(); -} - bool Type::isWebAssemblyExternrefType() const { if (const auto *BT = getAs()) return BT->getKind() == BuiltinType::WasmExternRef; return false; } +bool Type::isWebAssemblyTableType() const { + if (const auto *ATy = dyn_cast(this)) + return ATy->getElementType().isWebAssemblyReferenceType(); + + if (const auto *PTy = dyn_cast(this)) + return PTy->getPointeeType().isWebAssemblyReferenceType(); + + return false; +} + bool Type::isSizelessType() const { return isSizelessBuiltinType(); } bool Type::isSVESizelessBuiltinType() const { @@ -2656,6 +2662,18 @@ return RD->hasNonTrivialToPrimitiveCopyCUnion(); } +bool QualType::isWebAssemblyReferenceType() const { + return isWebAssemblyExternrefType() || isWebAssemblyFuncrefType(); +} + +bool QualType::isWebAssemblyExternrefType() const { + return getTypePtr()->isWebAssemblyExternrefType(); +} + +bool QualType::isWebAssemblyFuncrefType() const { + return getTypePtr()->isFunctionPointerType() && getAddressSpace() == LangAS::wasm_funcref; +} + QualType::PrimitiveDefaultInitializeKind QualType::isNonTrivialToPrimitiveDefaultInitialize() const { if (const auto *RT = diff --git a/clang/lib/Basic/Targets/WebAssembly.cpp b/clang/lib/Basic/Targets/WebAssembly.cpp --- a/clang/lib/Basic/Targets/WebAssembly.cpp +++ b/clang/lib/Basic/Targets/WebAssembly.cpp @@ -151,6 +151,7 @@ Features["atomics"] = true; Features["mutable-globals"] = true; Features["tail-call"] = true; + Features["reference-types"] = true; setSIMDLevel(Features, SIMD128, true); } else if (CPU == "generic") { Features["sign-ext"] = true; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -19483,6 +19483,95 @@ CGM.getIntrinsic(Intrinsic::wasm_relaxed_dot_bf16x8_add_f32); return Builder.CreateCall(Callee, {LHS, RHS, Acc}); } + case WebAssembly::BI__builtin_wasm_table_get: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Index = EmitScalarExpr(E->getArg(1)); + Function *Callee; + if (E->getType().isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_get_externref); + else if (E->getType().isWebAssemblyFuncrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_get_funcref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_get"); + return Builder.CreateCall(Callee, {Table, Index}); + } + case WebAssembly::BI__builtin_wasm_table_set: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Index = EmitScalarExpr(E->getArg(1)); + Value *Val = EmitScalarExpr(E->getArg(2)); + Function *Callee; + if (E->getArg(2)->getType().isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_set_externref); + else if (E->getArg(2)->getType().isWebAssemblyFuncrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_set_funcref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_set"); + return Builder.CreateCall(Callee, {Table, Index, Val}); + } + case WebAssembly::BI__builtin_wasm_table_size: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Value = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_table_size); + return Builder.CreateCall(Callee, Value); + } + case WebAssembly::BI__builtin_wasm_table_grow: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Val = EmitScalarExpr(E->getArg(1)); + Value *NElems = EmitScalarExpr(E->getArg(2)); + + Function *Callee; + if (E->getArg(1)->getType().isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_grow_externref); + else if (E->getArg(2)->getType().isWebAssemblyFuncrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_fill_funcref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_grow"); + + return Builder.CreateCall(Callee, {Table, Val, NElems}); + } + case WebAssembly::BI__builtin_wasm_table_fill: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *Table = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *Index = EmitScalarExpr(E->getArg(1)); + Value *Val = EmitScalarExpr(E->getArg(2)); + Value *NElems = EmitScalarExpr(E->getArg(3)); + + Function *Callee; + if (E->getArg(2)->getType().isWebAssemblyExternrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_fill_externref); + else if (E->getArg(2)->getType().isWebAssemblyFuncrefType()) + Callee = CGM.getIntrinsic(Intrinsic::wasm_table_fill_funcref); + else + llvm_unreachable( + "Unexpected reference type for __builtin_wasm_table_fill"); + + return Builder.CreateCall(Callee, {Table, Index, Val, NElems}); + } + case WebAssembly::BI__builtin_wasm_table_copy: { + assert(E->getArg(0)->getType()->isArrayType()); + Value *TableX = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(0)).getPointer()); + Value *TableY = + EmitCastToVoidPtr(EmitArrayToPointerDecay(E->getArg(1)).getPointer()); + Value *DstIdx = EmitScalarExpr(E->getArg(2)); + Value *SrcIdx = EmitScalarExpr(E->getArg(3)); + Value *NElems = EmitScalarExpr(E->getArg(4)); + + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_table_copy); + + return Builder.CreateCall(Callee, {TableX, TableY, SrcIdx, DstIdx, NElems}); + } default: return nullptr; } diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -33,9 +33,11 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/Intrinsics.h" +#include "llvm/IR/IntrinsicsWebAssembly.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/MDBuilder.h" #include "llvm/IR/MatrixBuilder.h" +#include "llvm/Passes/OptimizationLevel.h" #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" diff --git a/clang/lib/Sema/SemaCast.cpp b/clang/lib/Sema/SemaCast.cpp --- a/clang/lib/Sema/SemaCast.cpp +++ b/clang/lib/Sema/SemaCast.cpp @@ -2740,6 +2740,15 @@ } } + // WebAssembly tables cannot be cast. + QualType SrcType = SrcExpr.get()->getType(); + if (SrcType->isWebAssemblyTableType()) { + Self.Diag(OpRange.getBegin(), diag::err_wasm_cast_table) << 1 + << SrcExpr.get()->getSourceRange(); + SrcExpr = ExprError(); + return; + } + // C++ [expr.cast]p5: The conversions performed by // - a const_cast, // - a static_cast, @@ -2915,6 +2924,13 @@ return; QualType SrcType = SrcExpr.get()->getType(); + if (SrcType->isWebAssemblyTableType()) { + Self.Diag(OpRange.getBegin(), diag::err_wasm_cast_table) << 1 + << SrcExpr.get()->getSourceRange(); + SrcExpr = ExprError(); + return; + } + assert(!SrcType->isPlaceholderType()); checkAddressSpaceCast(SrcType, DestType); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4794,6 +4794,18 @@ return BuiltinWasmRefNullExtern(TheCall); case WebAssembly::BI__builtin_wasm_ref_null_func: return BuiltinWasmRefNullFunc(TheCall); + case WebAssembly::BI__builtin_wasm_table_get: + return BuiltinWasmTableGet(TheCall); + case WebAssembly::BI__builtin_wasm_table_set: + return BuiltinWasmTableSet(TheCall); + case WebAssembly::BI__builtin_wasm_table_size: + return BuiltinWasmTableSize(TheCall); + case WebAssembly::BI__builtin_wasm_table_grow: + return BuiltinWasmTableGrow(TheCall); + case WebAssembly::BI__builtin_wasm_table_fill: + return BuiltinWasmTableFill(TheCall); + case WebAssembly::BI__builtin_wasm_table_copy: + return BuiltinWasmTableCopy(TheCall); } return false; @@ -12291,6 +12303,10 @@ } } + if (RetValExp && RetValExp->getType()->isWebAssemblyTableType()) { + Diag(ReturnLoc, diag::err_wasm_table_art) << 1; + } + // PPC MMA non-pointer types are not allowed as return type. Checking the type // here prevent the user from using a PPC MMA type as trailing return type. if (Context.getTargetInfo().getTriple().isPPC64()) @@ -16011,6 +16027,13 @@ RD, /*DeclIsField*/ false); } } + + if (!Param->isInvalidDecl() && + Param->getOriginalType()->isWebAssemblyTableType()) { + Param->setInvalidDecl(); + HasInvalidParm = true; + Diag(Param->getLocation(), diag::err_wasm_table_as_function_parameter); + } } return HasInvalidParm; @@ -18293,6 +18316,168 @@ return CallResult; } +/// Checks the argument at the given index is a WebAssembly table and if it +/// is, sets ElTy to the element type. +static bool CheckWasmBuiltinArgIsTable(Sema &S, CallExpr *E, unsigned ArgIndex, + QualType &ElTy) { + Expr *ArgExpr = E->getArg(ArgIndex); + const auto *ATy = dyn_cast(ArgExpr->getType()); + if (!ATy || !ATy->getElementType().isWebAssemblyReferenceType()) { + return S.Diag(ArgExpr->getBeginLoc(), + diag::err_wasm_builtin_arg_must_be_table_type) + << ArgIndex + 1 << ArgExpr->getSourceRange(); + } + ElTy = ATy->getElementType(); + return false; +} + +/// Checks the argument at the given index is an integer. +static bool CheckWasmBuiltinArgIsInteger(Sema &S, CallExpr *E, + unsigned ArgIndex) { + Expr *ArgExpr = E->getArg(ArgIndex); + if (!ArgExpr->getType()->isIntegerType()) { + return S.Diag(ArgExpr->getBeginLoc(), + diag::err_wasm_builtin_arg_must_be_integer_type) + << ArgIndex + 1 << ArgExpr->getSourceRange(); + } + return false; +} + +/// Check that the first argument is a WebAssembly table, and the second +/// is an index to use as index into the table. +bool Sema::BuiltinWasmTableGet(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 2)) + return true; + + QualType ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 1)) + return true; + + // If all is well, we set the type of TheCall to be the type of the + // element of the table. + // i.e. a table.get on an externref table has type externref, + // or whatever the type of the table element is. + TheCall->setType(ElTy); + + return false; +} + +/// Check that the first argumnet is a WebAssembly table, the second is +/// an index to use as index into the table and the third is the reference +/// type to set into the table. +bool Sema::BuiltinWasmTableSet(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 3)) + return true; + + QualType ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 1)) + return true; + + if (!Context.hasSameType(ElTy, TheCall->getArg(2)->getType())) + return true; + + return false; +} + +/// Check that the argument is a WebAssembly table. +bool Sema::BuiltinWasmTableSize(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 1)) + return true; + + QualType ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is the +/// value to use for new elements (of a type matching the table type), the +/// third value is an integer. +bool Sema::BuiltinWasmTableGrow(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 3)) + return true; + + QualType ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + Expr *NewElemArg = TheCall->getArg(1); + if (!Context.hasSameType(ElTy, NewElemArg->getType())) { + return Diag(NewElemArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 2 << 1 << NewElemArg->getSourceRange(); + } + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 2)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is an +/// integer, the third is the value to use to fill the table (of a type +/// matching the table type), and the fourth is an integer. +bool Sema::BuiltinWasmTableFill(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 4)) + return true; + + QualType ElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, ElTy)) + return true; + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 1)) + return true; + + Expr *NewElemArg = TheCall->getArg(2); + if (!Context.hasSameType(ElTy, NewElemArg->getType())) { + return Diag(NewElemArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 3 << 1 << NewElemArg->getSourceRange(); + } + + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, 3)) + return true; + + return false; +} + +/// Check that the first argument is a WebAssembly table, the second is also a +/// WebAssembly table (of the same element type), and the third to fifth +/// arguments are integers. +bool Sema::BuiltinWasmTableCopy(CallExpr *TheCall) { + if (checkArgCount(*this, TheCall, 5)) + return true; + + QualType XElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 0, XElTy)) + return true; + + QualType YElTy; + if (CheckWasmBuiltinArgIsTable(*this, TheCall, 1, YElTy)) + return true; + + Expr *TableYArg = TheCall->getArg(1); + if (!Context.hasSameType(XElTy, YElTy)) { + return Diag(TableYArg->getBeginLoc(), + diag::err_wasm_builtin_arg_must_match_table_element_type) + << 2 << 1 << TableYArg->getSourceRange(); + } + + for (int I = 2; I <= 4; I++) { + if (CheckWasmBuiltinArgIsInteger(*this, TheCall, I)) + return true; + } + + return false; +} + /// \brief Enforce the bounds of a TCB /// CheckTCBEnforcement - Enforces that every function in a named TCB only /// directly calls other functions in the same TCB as marked by the enforce_tcb diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -7844,6 +7844,18 @@ } } + // WebAssembly tables are always in address space 1 (wasm_var). Don't apply + // address space if the table has local storage (semantic checks elsewhere + // will produce an error anyway). + if (const auto *ATy = dyn_cast(NewVD->getType())) { + if (ATy && ATy->getElementType().isWebAssemblyReferenceType() && + !NewVD->hasLocalStorage()) { + QualType Type = Context.getAddrSpaceQualType( + NewVD->getType(), Context.getLangASForBuiltinAddressSpace(1)); + NewVD->setType(Type); + } + } + // Handle attributes prior to checking for duplicates in MergeVarDecl ProcessDeclAttributes(S, NewVD, D); @@ -8000,6 +8012,13 @@ if (!IsVariableTemplateSpecialization) D.setRedeclaration(CheckVariableDeclaration(NewVD, Previous)); + // CheckVariableDeclaration will set NewVD as invalid if something is in + // error like WebAssembly tables being declared as arrays with a non-zero + // size, but then parsing continues and emits further errors on that line. + // To avoid that we check here if it happened and return nullptr. + if (NewVD->getType()->isWebAssemblyTableType() && NewVD->isInvalidDecl()) + return nullptr; + if (NewTemplate) { VarTemplateDecl *PrevVarTemplate = NewVD->getPreviousDecl() @@ -8606,6 +8625,28 @@ } } + // WebAssembly tables must be static with a zero length and can't be + // declared within functions. + if (T->isWebAssemblyTableType()) { + if (getCurScope()->getParent()) { // Parent is null at top-level + Diag(NewVD->getLocation(), diag::err_wasm_table_in_function); + NewVD->setInvalidDecl(); + return; + } + if (NewVD->getStorageClass() != SC_Static) { + Diag(NewVD->getLocation(), diag::err_wasm_table_must_be_static); + NewVD->setInvalidDecl(); + return; + } + const auto *ATy = dyn_cast(T.getTypePtr()); + if (!ATy || ATy->getSize().getSExtValue() != 0) { + Diag(NewVD->getLocation(), + diag::err_typecheck_wasm_table_must_have_zero_length); + NewVD->setInvalidDecl(); + return; + } + } + bool isVM = T->isVariablyModifiedType(); if (isVM || NewVD->hasAttr() || NewVD->hasAttr()) @@ -8677,7 +8718,7 @@ } if (!NewVD->hasLocalStorage() && T->isSizelessType() && - !T->isWebAssemblyReferenceType()) { + !T.isWebAssemblyReferenceType()) { Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T; NewVD->setInvalidDecl(); return; @@ -10685,6 +10726,14 @@ } } } + // WebAssembly tables can't be used as function parameters. + if (Context.getTargetInfo().getTriple().isWasm()) { + if (PT->getUnqualifiedDesugaredType()->isWebAssemblyTableType()) { + Diag(Param->getTypeSpecStartLoc(), + diag::err_wasm_table_as_function_parameter); + D.setInvalidType(); + } + } } // Here we have an function template explicit specialization at class scope. @@ -13068,6 +13117,14 @@ return; } + // WebAssembly tables can't be used to initialise a variable. + if (Init && !Init->getType().isNull() && + Init->getType()->isWebAssemblyTableType()) { + Diag(Init->getExprLoc(), diag::err_wasm_table_art) << 0; + VDecl->setInvalidDecl(); + return; + } + // C++11 [decl.spec.auto]p6. Deduce the type which 'auto' stands in for. if (VDecl->getType()->isUndeducedType()) { // Attempt typo correction early so that the type of the init expression can diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -16474,6 +16474,11 @@ if (!Invalid && (Mode == 0 || !BaseType->isVoidType()) && !BaseType->isDependentType() && RequireCompleteType(Loc, BaseType, DK)) Invalid = true; + + if (!Invalid && BaseType.isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_reftype_tc) << 1; + Invalid = true; + } if (!Invalid && Mode != 1 && BaseType->isSizelessType()) { Diag(Loc, diag::err_catch_sizeless) << (Mode == 2 ? 1 : 0) << BaseType; diff --git a/clang/lib/Sema/SemaExceptionSpec.cpp b/clang/lib/Sema/SemaExceptionSpec.cpp --- a/clang/lib/Sema/SemaExceptionSpec.cpp +++ b/clang/lib/Sema/SemaExceptionSpec.cpp @@ -172,6 +172,12 @@ RequireCompleteType(Range.getBegin(), PointeeT, DiagID, Kind, Range)) return ReturnValueOnError; + // WebAssembly reference types can't be used in exception specifications. + if (PointeeT.isWebAssemblyReferenceType()) { + Diag(Range.getBegin(), diag::err_wasm_reftype_exception_spec); + return true; + } + // The MSVC compatibility mode doesn't extend to sizeless types, // so diagnose them separately. if (PointeeT->isSizelessType() && Kind != 1) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -941,7 +941,7 @@ return VAK_Invalid; if (Context.getTargetInfo().getTriple().isWasm() && - Ty->isWebAssemblyReferenceType()) { + Ty.isWebAssemblyReferenceType()) { return VAK_Invalid; } @@ -4297,6 +4297,15 @@ E->getSourceRange(), ExprKind)) return false; + // WebAssembly tables are always illegal operands to unary expressions and + // type traits. + if (Context.getTargetInfo().getTriple().isWasm() && + E->getType()->isWebAssemblyTableType()) { + Diag(E->getExprLoc(), diag::err_wasm_table_invalid_uett_operand) + << getTraitSpelling(ExprKind); + return true; + } + // 'alignof' applied to an expression only requires the base element type of // the expression to be complete. 'sizeof' requires the expression's type to // be complete (and will attempt to complete it if it's an array of unknown @@ -4412,6 +4421,15 @@ return true; } + // WebAssembly tables are always illegal operands to unary expressions and + // type traits. + if (Context.getTargetInfo().getTriple().isWasm() && + ExprType->isWebAssemblyTableType()) { + Diag(OpLoc, diag::err_wasm_table_invalid_uett_operand) + << getTraitSpelling(ExprKind); + return true; + } + if (CheckObjCTraitOperandConstraints(*this, ExprType, OpLoc, ExprRange, ExprKind)) return true; @@ -4874,6 +4892,12 @@ ArgExprs.front(), rbLoc); } + if (base->getType()->isWebAssemblyTableType()) { + Diag(base->getExprLoc(), diag::err_wasm_table_art) + << SourceRange(base->getBeginLoc(), rbLoc) << 3; + return ExprError(); + } + // Handle any non-overload placeholder types in the base and index // expressions. We can't handle overloads here because the other // operand might be an overloadable type, in which case the overload @@ -5829,6 +5853,7 @@ if (!ResultType.hasQualifiers()) VK = VK_PRValue; } else if (!ResultType->isDependentType() && + !ResultType.isWebAssemblyReferenceType() && RequireCompleteSizedType( LLoc, ResultType, diag::err_subscript_incomplete_or_sizeless_type, BaseExpr)) @@ -7349,6 +7374,16 @@ TheCall->setType(FuncT->getCallResultType(Context)); TheCall->setValueKind(Expr::getValueKindForType(FuncT->getReturnType())); + // WebAssembly tables can't be used as arguments. + if (Context.getTargetInfo().getTriple().isWasm()) { + for (const Expr *Arg : Args) { + if (Arg && Arg->getType()->isWebAssemblyTableType()) { + return ExprError( + Diag(Arg->getExprLoc(), diag::err_wasm_table_as_function_parameter)); + } + } + } + if (Proto) { if (ConvertArgumentsForCall(TheCall, Fn, FDecl, Proto, Args, RParenLoc, IsExecConfig)) @@ -8919,8 +8954,14 @@ if (LHS.isInvalid() || RHS.isInvalid()) return QualType(); + // WebAssembly tables are not allowed as conditional LHS or RHS. QualType LHSTy = LHS.get()->getType(); QualType RHSTy = RHS.get()->getType(); + if (LHSTy->isWebAssemblyTableType() || RHSTy->isWebAssemblyTableType()) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } // Diagnose attempts to convert between __ibm128, __float128 and long double // where such conversions currently can't be handled. @@ -12338,6 +12379,11 @@ S.inTemplateInstantiation()) return; + // WebAssembly Tables cannot be compared, therefore shouldn't emit + // Tautological diagnostics. + if (LHSType->isWebAssemblyTableType() || RHSType->isWebAssemblyTableType()) + return; + // Comparisons between two array types are ill-formed for operator<=>, so // we shouldn't emit any additional warnings about it. if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType()) @@ -12724,6 +12770,12 @@ (RHSType->isArithmeticType() || RHSType->isEnumeralType())) return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); + if ((LHSType->isPointerType() && + LHSType->getPointeeType().isWebAssemblyReferenceType()) || + (RHSType->isPointerType() && + RHSType->getPointeeType().isWebAssemblyReferenceType())) + return InvalidOperands(Loc, LHS, RHS); + const Expr::NullPointerConstantKind LHSNullKind = LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); const Expr::NullPointerConstantKind RHSNullKind = @@ -13642,6 +13694,16 @@ if (EnumConstantInBoolContext) Diag(Loc, diag::warn_enum_constant_in_bool_context); + // WebAssembly tables can't be used with logical operators. + QualType LHSTy = LHS.get()->getType(); + QualType RHSTy = RHS.get()->getType(); + const auto *LHSATy = dyn_cast(LHSTy); + const auto *RHSATy = dyn_cast(RHSTy); + if ((LHSATy && LHSATy->getElementType().isWebAssemblyReferenceType()) || + (RHSATy && RHSATy->getElementType().isWebAssemblyReferenceType())) { + return InvalidOperands(Loc, LHS, RHS); + } + // Diagnose cases where the user write a logical and/or but probably meant a // bitwise one. We do this when the LHS is a non-bool integer and the RHS // is a constant. @@ -14183,6 +14245,12 @@ return QualType(); } + // WebAssembly tables can't be used on RHS of an assignment expression. + if (RHSType->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_art) << 0; + return QualType(); + } + AssignConvertType ConvTy; if (CompoundType.isNull()) { Expr *RHSCheck = RHS.get(); @@ -14790,11 +14858,19 @@ if (op->getType()->isObjCObjectType()) return Context.getObjCObjectPointerType(op->getType()); - if (Context.getTargetInfo().getTriple().isWasm() && - op->getType()->isWebAssemblyReferenceType()) { - Diag(OpLoc, diag::err_wasm_ca_reference) - << 1 << OrigOp.get()->getSourceRange(); - return QualType(); + // Cannot take the address of WebAssembly references or tables. + if (Context.getTargetInfo().getTriple().isWasm()) { + QualType OpTy = op->getType(); + if (OpTy.isWebAssemblyReferenceType()) { + Diag(OpLoc, diag::err_wasm_ca_reference) + << 1 << OrigOp.get()->getSourceRange(); + return QualType(); + } + if (OpTy->isWebAssemblyTableType()) { + Diag(OpLoc, diag::err_wasm_table_pr) << 1 + << OrigOp.get()->getSourceRange(); + return QualType(); + } } CheckAddressOfPackedMember(op); @@ -15979,6 +16055,13 @@ resultType = Context.FloatTy; } + // WebAsembly tables can't be used in unary expressions. + if (resultType->isPointerType() && + resultType->getPointeeType().isWebAssemblyReferenceType()) { + return ExprError(Diag(OpLoc, diag::err_typecheck_unary_expr) + << resultType << Input.get()->getSourceRange()); + } + if (resultType->isDependentType()) break; if (resultType->isScalarType() && !isScopedEnumerationType(resultType)) { @@ -18932,7 +19015,7 @@ } if (BuildAndDiagnose && S.Context.getTargetInfo().getTriple().isWasm() && - CaptureType.getNonReferenceType()->isWebAssemblyReferenceType()) { + CaptureType.getNonReferenceType().isWebAssemblyReferenceType()) { S.Diag(Loc, diag::err_wasm_ca_reference) << 0; Invalid = true; } diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -975,6 +975,19 @@ Ty = Ptr->getPointeeType(); isPointer = true; } + + // Cannot throw WebAssembly reference type. + if (Ty.isWebAssemblyReferenceType()) { + Diag(ThrowLoc, diag::err_wasm_reftype_tc) << 0 << E->getSourceRange(); + return true; + } + + // Cannot throw WebAssembly table. + if (isPointer && Ty.isWebAssemblyReferenceType()) { + Diag(ThrowLoc, diag::err_wasm_table_art) << 2 << E->getSourceRange(); + return true; + } + if (!isPointer || !Ty->isVoidType()) { if (RequireCompleteType(ThrowLoc, Ty, isPointer ? diag::err_throw_incomplete_ptr @@ -6560,6 +6573,13 @@ if (IsSizelessVectorConditional) return CheckSizelessVectorConditionalTypes(Cond, LHS, RHS, QuestionLoc); + // WebAssembly tables are not allowed as conditional LHS or RHS. + if (LTy->isWebAssemblyTableType() || RTy->isWebAssemblyTableType()) { + Diag(QuestionLoc, diag::err_wasm_table_conditional_expression) + << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); + return QualType(); + } + // C++11 [expr.cond]p3 // Otherwise, if the second and third operand have different types, and // either has (cv) class type [...] an attempt is made to convert each of diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -11997,7 +11997,16 @@ S.Diag(PD.first, PD.second, shouldDeferDiags(S, Args, OpLoc)); - NoteCandidates(S, Args, Cands, Opc, OpLoc); + // In WebAssembly we don't want to emit further diagnostics if a table is + // passed as an argument to a function. + bool NoteCands = true; + for (const Expr *Arg : Args) { + if (Arg->getType()->isWebAssemblyTableType()) + NoteCands = false; + } + + if (NoteCands) + NoteCandidates(S, Args, Cands, Opc, OpLoc); if (OCD == OCD_AmbiguousCandidates) MaybeDiagnoseAmbiguousConstraints(S, {begin(), end()}); diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -3969,6 +3969,14 @@ } else // If we don't have a function/method context, bail. return StmtError(); + if (RetValExp) { + const auto *ATy = dyn_cast(RetValExp->getType()); + if (ATy && ATy->getElementType().isWebAssemblyReferenceType()) { + Diag(ReturnLoc, diag::err_wasm_table_art) << 1; + return StmtError(); + } + } + // C++1z: discarded return statements are not considered when deducing a // return type. if (ExprEvalContexts.back().isDiscardedStatementContext() && diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -2198,11 +2198,19 @@ if (getLangOpts().OpenCL) T = deduceOpenCLPointeeAddrSpace(*this, T); - // In WebAssembly, pointers to reference types are illegal. - if (getASTContext().getTargetInfo().getTriple().isWasm() && - T->isWebAssemblyReferenceType()) { - Diag(Loc, diag::err_wasm_reference_pr) << 0; - return QualType(); + // In WebAssembly, pointers to reference types and pointers to tables are + // illegal. + if (getASTContext().getTargetInfo().getTriple().isWasm()) { + if (T.isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_reference_pr) << 0; + return QualType(); + } + + // We need to desugar the type here in case T is a ParenType. + if (T->getUnqualifiedDesugaredType()->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_pr) << 0; + return QualType(); + } } // Build the pointer type. @@ -2280,12 +2288,16 @@ if (getLangOpts().OpenCL) T = deduceOpenCLPointeeAddrSpace(*this, T); - // In WebAssembly, references to reference types are illegal. + // In WebAssembly, references to reference types and tables are illegal. if (getASTContext().getTargetInfo().getTriple().isWasm() && - T->isWebAssemblyReferenceType()) { + T.isWebAssemblyReferenceType()) { Diag(Loc, diag::err_wasm_reference_pr) << 1; return QualType(); } + if (T->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_pr) << 1; + return QualType(); + } // Handle restrict on references. if (LValueRef) @@ -2490,12 +2502,22 @@ } else { // C99 6.7.5.2p1: If the element type is an incomplete or function type, // reject it (e.g. void ary[7], struct foo ary[7], void ary[7]()) - if (RequireCompleteSizedType(Loc, T, + if (!T.isWebAssemblyReferenceType() && + RequireCompleteSizedType(Loc, T, diag::err_array_incomplete_or_sizeless_type)) return QualType(); } - if (T->isSizelessType()) { + // Multi-dimensional arrays of WebAssembly references are not allowed. + if (Context.getTargetInfo().getTriple().isWasm() && T->isArrayType()) { + const auto *ATy = dyn_cast(T); + if (ATy && ATy->getElementType().isWebAssemblyReferenceType()) { + Diag(Loc, diag::err_wasm_reftype_multidimensional_array); + return QualType(); + } + } + + if (T->isSizelessType() && !T.isWebAssemblyReferenceType()) { Diag(Loc, diag::err_array_incomplete_or_sizeless_type) << 1 << T; return QualType(); } @@ -2614,7 +2636,7 @@ << ArraySize->getSourceRange(); return QualType(); } - if (ConstVal == 0) { + if (ConstVal == 0 && !T.isWebAssemblyReferenceType()) { // GCC accepts zero sized static arrays. We allow them when // we're not in a SFINAE context. Diag(ArraySize->getBeginLoc(), @@ -3003,6 +3025,9 @@ Diag(Loc, diag::err_parameters_retval_cannot_have_fp16_type) << 0 << FixItHint::CreateInsertion(Loc, "*"); Invalid = true; + } else if (ParamType->isWebAssemblyTableType()) { + Diag(Loc, diag::err_wasm_table_as_function_parameter); + Invalid = true; } // C++2a [dcl.fct]p4: diff --git a/clang/test/CodeGen/WebAssembly/builtins-table.c b/clang/test/CodeGen/WebAssembly/builtins-table.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/builtins-table.c @@ -0,0 +1,67 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py UTC_ARGS: --function-signature +// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -disable-O0-optnone -emit-llvm %s -o - | opt -S -passes=mem2reg | FileCheck %s +// REQUIRES: webassembly-registered-target + +static __externref_t table[0]; + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_get +// CHECK-SAME: (i32 noundef [[INDEX:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(10) @llvm.wasm.table.get.externref(ptr addrspace(1) @table, i32 [[INDEX]]) +// CHECK-NEXT: ret ptr addrspace(10) [[TMP0]] +// +__externref_t test_builtin_wasm_table_get(int index) { + return __builtin_wasm_table_get(table, index); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_set +// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.wasm.table.set.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]]) +// CHECK-NEXT: ret void +// +void test_builtin_wasm_table_set(int index, __externref_t ref) { + return __builtin_wasm_table_set(table, index, ref); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_size +// CHECK-SAME: () #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.size(ptr addrspace(1) @table) +// CHECK-NEXT: ret i32 [[TMP0]] +// +int test_builtin_wasm_table_size() { + return __builtin_wasm_table_size(table); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_grow +// CHECK-SAME: (ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call i32 @llvm.wasm.table.grow.externref(ptr addrspace(1) @table, ptr addrspace(10) [[REF]], i32 [[NELEM]]) +// CHECK-NEXT: ret i32 [[TMP0]] +// +int test_builtin_wasm_table_grow(__externref_t ref, int nelem) { + return __builtin_wasm_table_grow(table, ref, nelem); +} + +// CHECK-LABEL: define {{[^@]+}}@test_builtin_wasm_table_fill +// CHECK-SAME: (i32 noundef [[INDEX:%.*]], ptr addrspace(10) [[REF:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.wasm.table.fill.externref(ptr addrspace(1) @table, i32 [[INDEX]], ptr addrspace(10) [[REF]], i32 [[NELEM]]) +// CHECK-NEXT: ret void +// +void test_builtin_wasm_table_fill(int index, __externref_t ref, int nelem) { + __builtin_wasm_table_fill(table, index, ref, nelem); +} + +static __externref_t other_table[0]; + +// CHECK-LABEL: define {{[^@]+}}@test_table_copy +// CHECK-SAME: (i32 noundef [[DST_IDX:%.*]], i32 noundef [[SRC_IDX:%.*]], i32 noundef [[NELEM:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: entry: +// CHECK-NEXT: call void @llvm.wasm.table.copy(ptr addrspace(1) @table, ptr addrspace(1) @other_table, i32 [[SRC_IDX]], i32 [[DST_IDX]], i32 [[NELEM]]) +// CHECK-NEXT: ret void +// +void test_table_copy(int dst_idx, int src_idx, int nelem) { + __builtin_wasm_table_copy(table, other_table, dst_idx, src_idx, nelem); +} diff --git a/clang/test/Sema/builtins-wasm.c b/clang/test/Sema/builtins-wasm.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/builtins-wasm.c @@ -0,0 +1,52 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s + +#define EXPR_HAS_TYPE(expr, type) _Generic((expr), type : 1, default : 0) + +static __externref_t table[0]; + +typedef void (*__funcref funcref_t)(); +void test_ref_null() { + funcref_t func = __builtin_wasm_ref_null_func(0); // expected-error {{too many arguments to function call, expected 0, have 1}} +} + +void test_table_size(__externref_t ref, void *ptr, int arr[]) { + __builtin_wasm_table_size(); // expected-error {{too few arguments to function call, expected 1, have 0}} + __builtin_wasm_table_size(1); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(ref); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(ptr); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(arr); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_size(table, table); // expected-error {{too many arguments to function call, expected 1, have 2}} + + _Static_assert(EXPR_HAS_TYPE(__builtin_wasm_table_size(table), int), ""); +} + +void test_table_grow(__externref_t ref, int size) { + __builtin_wasm_table_grow(); // expected-error {{too few arguments to function call, expected 3, have 0}} + __builtin_wasm_table_grow(table, table, table, table); // expected-error {{too many arguments to function call, expected 3, have 4}} + __builtin_wasm_table_grow(ref, ref, size); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_grow(table, table, size); // expected-error {{2nd argument must match the element type of the WebAssembly table in the 1st argument}} + __builtin_wasm_table_grow(table, ref, table); // expected-error {{3rd argument must be an integer}} + + _Static_assert(EXPR_HAS_TYPE(__builtin_wasm_table_grow(table, ref, size), int), ""); +} + +void test_table_fill(int index, __externref_t ref, int nelem) { + __builtin_wasm_table_fill(); // expected-error {{too few arguments to function call, expected 4, have 0}} + __builtin_wasm_table_fill(table, table, table, table, table); // expected-error {{too many arguments to function call, expected 4, have 5}} + __builtin_wasm_table_fill(index, index, ref, nelem); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_fill(table, table, ref, nelem); // expected-error {{2nd argument must be an integer}} + __builtin_wasm_table_fill(table, index, index, ref); // expected-error {{3rd argument must match the element type of the WebAssembly table in the 1st argument}} + __builtin_wasm_table_fill(table, index, ref, table); // expected-error {{4th argument must be an integer}} + __builtin_wasm_table_fill(table, index, ref, nelem); +} + +void test_table_copy(int dst_idx, int src_idx, int nelem) { + __builtin_wasm_table_copy(); // expected-error {{too few arguments to function call, expected 5, have 0}} + __builtin_wasm_table_copy(table, table, table, table, table, table); // expected-error {{too many arguments to function call, expected 5, have 6}} + __builtin_wasm_table_copy(src_idx, table, dst_idx, src_idx, nelem); // expected-error {{1st argument must be a WebAssembly table}} + __builtin_wasm_table_copy(table, src_idx, dst_idx, src_idx, nelem); // expected-error {{2nd argument must be a WebAssembly table}} + __builtin_wasm_table_copy(table, table, table, src_idx, nelem); // expected-error {{3rd argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, table, nelem); // expected-error {{4th argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, src_idx, table); // expected-error {{5th argument must be an integer}} + __builtin_wasm_table_copy(table, table, dst_idx, src_idx, nelem); +} diff --git a/clang/test/Sema/wasm-refs-and-table-ped.c b/clang/test/Sema/wasm-refs-and-table-ped.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/wasm-refs-and-table-ped.c @@ -0,0 +1,3 @@ +// RUN: %clang_cc1 -fsyntax-only -pedantic -verify=expected -triple wasm32 -Wno-unused-value -target-feature +reference-types %s +// No error should be emitted. +static __externref_t table[0]; // expected-no-diagnostics diff --git a/clang/test/Sema/wasm-refs-and-tables.c b/clang/test/Sema/wasm-refs-and-tables.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/wasm-refs-and-tables.c @@ -0,0 +1,133 @@ +// RUN: %clang_cc1 -fsyntax-only -verify=expected,conly -triple wasm32 -Wno-unused-value -target-feature +reference-types %s +// RUN: %clang_cc1 -x c++ -std=c++17 -fsyntax-only -verify=expected,cpp -triple wasm32 -Wno-unused-value -target-feature +reference-types %s + +// Note: As WebAssembly references are sizeless types, we don't exhaustively +// test for cases covered by sizeless-1.c and similar tests. + +// Unlike standard sizeless types, reftype globals are supported. +__externref_t r1; +extern __externref_t r2; +static __externref_t r3; + +__externref_t *t1; // expected-error {{pointer to WebAssembly reference type is not allowed}} +__externref_t **t2; // expected-error {{pointer to WebAssembly reference type is not allowed}} +__externref_t ******t3; // expected-error {{pointer to WebAssembly reference type is not allowed}} +static __externref_t t4[3]; // expected-error {{only zero-length WebAssembly tables are currently supported}} +static __externref_t t5[]; // expected-error {{only zero-length WebAssembly tables are currently supported}} +static __externref_t t6[] = {0}; // expected-error {{only zero-length WebAssembly tables are currently supported}} +__externref_t t7[0]; // expected-error {{WebAssembly table must be static}} +static __externref_t t8[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} +static __externref_t (*t9)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} + +static __externref_t table[0]; +static __externref_t other_table[0] = {}; +static __externref_t another_table[] = {}; // expected-error {{only zero-length WebAssembly tables are currently supported}} + +struct s { + __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} + __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}} + __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}} + __externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} +}; + +union u { + __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f2[0]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f3[]; // expected-error {{field has sizeless type '__externref_t'}} + __externref_t f4[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} + __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}} + __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}} + __externref_t (*f7)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} +}; + +void illegal_argument_1(__externref_t table[]); // expected-error {{cannot use WebAssembly table as a function parameter}} +void illegal_argument_2(__externref_t table[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} +void illegal_argument_3(__externref_t *table); // expected-error {{pointer to WebAssembly reference type is not allowed}} +void illegal_argument_4(__externref_t ***table); // expected-error {{pointer to WebAssembly reference type is not allowed}} +void illegal_argument_5(__externref_t (*table)[0]); // expected-error {{cannot form a pointer to a WebAssembly table}} +void illegal_argument_6(__externref_t table[0]); // expected-error {{cannot use WebAssembly table as a function parameter}} + +__externref_t *illegal_return_1(); // expected-error {{pointer to WebAssembly reference type is not allowed}} +__externref_t ***illegal_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}} +__externref_t (*illegal_return_3())[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} + +void varargs(int, ...); +typedef void (*__funcref funcref_t)(); +typedef void (*__funcref __funcref funcref_fail_t)(); // expected-warning {{attribute '__funcref' is already applied}} + +__externref_t func(__externref_t ref) { + &ref; // expected-error {{cannot take address of WebAssembly reference}} + int foo = 40; + (__externref_t *)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} + (__externref_t ****)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} + sizeof(ref); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} + sizeof(__externref_t); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} + sizeof(__externref_t[0]); // expected-error {{invalid application of 'sizeof' to WebAssembly table}} + sizeof(table); // expected-error {{invalid application of 'sizeof' to WebAssembly table}} + sizeof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} + sizeof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} + sizeof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}}; + // expected-warning@+1 {{'_Alignof' applied to an expression is a GNU extension}} + _Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(__externref_t[]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(__externref_t[0]); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} + _Alignof(table); // expected-warning {{'_Alignof' applied to an expression is a GNU extension}} expected-error {{invalid application of 'alignof' to WebAssembly table}} + _Alignof(__externref_t[0][0]); // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} + _Alignof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} + _Alignof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}}; + varargs(1, ref); // expected-error {{cannot pass expression of type '__externref_t' to variadic function}} + + __externref_t lt1[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + static __externref_t lt2[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + static __externref_t lt3[0][0]; // expected-error {{multi-dimensional arrays of WebAssembly references are not allowed}} + static __externref_t(*lt4)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} + // conly-error@+2 {{cannot use WebAssembly table as a function parameter}} + // cpp-error@+1 {{no matching function for call to 'illegal_argument_1'}} + illegal_argument_1(table); + varargs(1, table); // expected-error {{cannot use WebAssembly table as a function parameter}} + table == 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} + 1 >= table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} + table == other_table; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and '__attribute__((address_space(1))) __externref_t[0]')}} + table !=- table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}} + !table; // expected-error {{invalid argument type '__attribute__((address_space(1))) __externref_t *' to unary expression}} + 1 && table; // expected-error {{invalid operands to binary expression ('int' and '__attribute__((address_space(1))) __externref_t[0]')}} + table || 1; // expected-error {{invalid operands to binary expression ('__attribute__((address_space(1))) __externref_t[0]' and 'int')}} + 1 ? table : table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}} + table ? : other_table; // expected-error {{cannot use a WebAssembly table within a branch of a conditional expression}} + (void *)table; // expected-error {{cannot cast from a WebAssembly table}} + void *u; + u = table; // expected-error {{cannot assign a WebAssembly table}} + void *v = table; // expected-error {{cannot assign a WebAssembly table}} + &table; // expected-error {{cannot form a reference to a WebAssembly table}} + (void)table; + + table[0]; // expected-error {{cannot subscript a WebAssembly table}} + table[0] = ref; // expected-error {{cannot subscript a WebAssembly table}} + + int i = 0; + __externref_t oh_no_vlas[i]; // expected-error {{WebAssembly table cannot be declared within a function}} + + return ref; +} + +void foo() { + static __externref_t t[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + { + static __externref_t t2[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + for (;;) { + static __externref_t t3[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + } + } + int i = ({ + static __externref_t t4[0]; // expected-error {{WebAssembly table cannot be declared within a function}} + 1; + }); +} + +void *ret_void_ptr() { + return table; // expected-error {{cannot return a WebAssembly table}} +} diff --git a/clang/test/Sema/wasm-refs.c b/clang/test/Sema/wasm-refs.c deleted file mode 100644 --- a/clang/test/Sema/wasm-refs.c +++ /dev/null @@ -1,75 +0,0 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s - -// Note: As WebAssembly references are sizeless types, we don't exhaustively -// test for cases covered by sizeless-1.c and similar tests. - -// Unlike standard sizeless types, reftype globals are supported. -__externref_t r1; -extern __externref_t r2; -static __externref_t r3; - -__externref_t *t1; // expected-error {{pointer to WebAssembly reference type is not allowed}} -__externref_t **t2; // expected-error {{pointer to WebAssembly reference type is not allowed}} -__externref_t ******t3; // expected-error {{pointer to WebAssembly reference type is not allowed}} -static __externref_t t4[3]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t5[]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t6[] = {0}; // expected-error {{array has sizeless element type '__externref_t'}} -__externref_t t7[0]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t t8[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} - -static __externref_t table[0]; // expected-error {{array has sizeless element type '__externref_t'}} - -struct s { - __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} - __externref_t f2[0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f3[]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f4[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}} - __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}} -}; - -union u { - __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} - __externref_t f2[0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f3[]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f4[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}} - __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}} -}; - -void illegal_argument_1(__externref_t table[]); // expected-error {{array has sizeless element type '__externref_t'}} -void illegal_argument_2(__externref_t table[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} -void illegal_argument_3(__externref_t *table); // expected-error {{pointer to WebAssembly reference type is not allowed}} -void illegal_argument_4(__externref_t ***table); // expected-error {{pointer to WebAssembly reference type is not allowed}} - -__externref_t *illegal_return_1(); // expected-error {{pointer to WebAssembly reference type is not allowed}} -__externref_t ***illegal_return_2(); // expected-error {{pointer to WebAssembly reference type is not allowed}} - -void varargs(int, ...); -typedef void (*__funcref funcref_t)(); -typedef void (*__funcref __funcref funcref_fail_t)(); // expected-warning {{attribute '__funcref' is already applied}} - -__externref_t func(__externref_t ref) { - &ref; // expected-error {{cannot take address of WebAssembly reference}} - int foo = 40; - (__externref_t *)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} - (__externref_t ****)(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} - sizeof(ref); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} - sizeof(__externref_t); // expected-error {{invalid application of 'sizeof' to sizeless type '__externref_t'}} - sizeof(__externref_t[0]); // expected-error {{array has sizeless element type '__externref_t'}} - sizeof(__externref_t[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} - sizeof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} - sizeof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}}; - // expected-warning@+1 {{'_Alignof' applied to an expression is a GNU extension}} - _Alignof(ref); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - _Alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - _Alignof(__externref_t[]); // expected-error {{array has sizeless element type '__externref_t'}} - _Alignof(__externref_t[0][0]); // expected-error {{array has sizeless element type '__externref_t'}} - _Alignof(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} - _Alignof(__externref_t ***); // expected-error {{pointer to WebAssembly reference type is not allowed}}; - varargs(1, ref); // expected-error {{cannot pass expression of type '__externref_t' to variadic function}} - - funcref_t func = __builtin_wasm_ref_null_func(0); // expected-error {{too many arguments to function call, expected 0, have 1}} - - return ref; -} diff --git a/clang/test/SemaCXX/wasm-refs-and-tables.cpp b/clang/test/SemaCXX/wasm-refs-and-tables.cpp new file mode 100644 --- /dev/null +++ b/clang/test/SemaCXX/wasm-refs-and-tables.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fsyntax-only -verify -triple wasm32 -Wno-unused-value -target-feature +reference-types %s +// RUN: %clang_cc1 -std=c++20 -fcxx-exceptions -fexceptions -fsyntax-only -verify -triple wasm32 -Wno-unused-value -target-feature +reference-types %s + +// +// Note: As WebAssembly references are sizeless types, we don't exhaustively +// test for cases covered by sizeless-1.c and similar tests. + +// Using c++11 to test dynamic exception specifications (which are not +// allowed in c++17). + +// Unlike standard sizeless types, reftype globals are supported. +__externref_t r1; +static __externref_t table[0]; + +#if (_cplusplus == 201103L) +__externref_t func(__externref_t ref) throw(__externref_t) { // expected-error {{WebAssembly reference type not allowed in exception specification}} + return ref; +} +#endif + +void *ret_void_ptr() { + throw table; // expected-error {{cannot throw a WebAssembly reference type}} + throw r1; // expected-error {{cannot throw a WebAssembly reference type}} + try {} + catch (__externref_t T) { // expected-error {{cannot catch a WebAssembly reference type}} + (void)0; + } + + return table; // expected-error {{cannot return a WebAssembly table}} +} + +#if (__cplusplus >= 202002L) +// Testing C++20 coroutines +task<__externref_t[]> g() { + co_return table; +} +#endif diff --git a/clang/test/SemaCXX/wasm-refs.cpp b/clang/test/SemaCXX/wasm-refs.cpp deleted file mode 100644 --- a/clang/test/SemaCXX/wasm-refs.cpp +++ /dev/null @@ -1,103 +0,0 @@ -// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify -std=gnu++11 -triple wasm32 -Wno-unused-value -target-feature +reference-types %s - -// This file tests C++ specific constructs with WebAssembly references and -// tables. See wasm-refs-and-tables.c for C constructs. - -__externref_t ref; -__externref_t &ref_ref1 = ref; // expected-error {{reference to WebAssembly reference type is not allowed}} -__externref_t &ref_ref2(ref); // expected-error {{reference to WebAssembly reference type is not allowed}} - -static __externref_t table[0]; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t (&ref_to_table1)[0] = table; // expected-error {{array has sizeless element type '__externref_t'}} -static __externref_t (&ref_to_table2)[0](table); // expected-error {{array has sizeless element type '__externref_t'}} - -void illegal_argument_1(__externref_t &r); // expected-error {{reference to WebAssembly reference type is not allowed}} -void illegal_argument_2(__externref_t (&t)[0]); // expected-error {{array has sizeless element type '__externref_t'}} - -__externref_t &illegal_return_1(); // expected-error {{reference to WebAssembly reference type is not allowed}} -__externref_t (&illegal_return_2())[0]; // expected-error {{array has sizeless element type '__externref_t'}} - -void illegal_throw1() throw(__externref_t); // expected-error {{sizeless type '__externref_t' is not allowed in exception specification}} -void illegal_throw2() throw(__externref_t *); // expected-error {{pointer to WebAssembly reference type is not allowed}} -void illegal_throw3() throw(__externref_t &); // expected-error {{reference to WebAssembly reference type is not allowed}} -void illegal_throw4() throw(__externref_t[0]); // expected-error {{array has sizeless element type '__externref_t'}} - -class RefClass { - __externref_t f1; // expected-error {{field has sizeless type '__externref_t'}} - __externref_t f2[0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f3[]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t f4[0][0]; // expected-error {{array has sizeless element type '__externref_t'}} - __externref_t *f5; // expected-error {{pointer to WebAssembly reference type is not allowed}} - __externref_t ****f6; // expected-error {{pointer to WebAssembly reference type is not allowed}} - __externref_t (*f7)[0]; // expected-error {{array has sizeless element type '__externref_t'}} -}; - -struct AStruct {}; - -template -struct TemplatedStruct { - T f; // expected-error {{field has sizeless type '__externref_t'}} - void foo(T); - T bar(void); - T arr[0]; // expected-error {{array has sizeless element type '__externref_t'}} - T *ptr; // expected-error {{pointer to WebAssembly reference type is not allowed}} -}; - -void func() { - int foo = 40; - static_cast<__externref_t>(foo); // expected-error {{static_cast from 'int' to '__externref_t' is not allowed}} - static_cast<__externref_t *>(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} - static_cast(ref); // expected-error {{static_cast from '__externref_t' to 'int' is not allowed}} - __externref_t(10); // expected-error {{functional-style cast from 'int' to '__externref_t' is not allowed}} - int i(ref); // expected-error {{cannot initialize a variable of type 'int' with an lvalue of type '__externref_t'}} - const_cast<__externref_t[0]>(table); // expected-error {{array has sizeless element type '__externref_t'}} - const_cast<__externref_t *>(table); // expected-error {{pointer to WebAssembly reference type is not allowed}} - reinterpret_cast<__externref_t>(foo); // expected-error {{reinterpret_cast from 'int' to '__externref_t' is not allowed}} - reinterpret_cast(ref); // expected-error {{reinterpret_cast from '__externref_t' to 'int' is not allowed}} - int iarr[0]; - reinterpret_cast<__externref_t[0]>(iarr); // expected-error {{array has sizeless element type '__externref_t'}} - reinterpret_cast<__externref_t *>(iarr); // expected-error {{pointer to WebAssembly reference type is not allowed}} - dynamic_cast<__externref_t>(foo); // expected-error {{invalid target type '__externref_t' for dynamic_cast; target type must be a reference or pointer type to a defined class}} - dynamic_cast<__externref_t *>(&foo); // expected-error {{pointer to WebAssembly reference type is not allowed}} - - TemplatedStruct<__externref_t> ts1; // expected-note {{in instantiation}} - TemplatedStruct<__externref_t *> ts2; // expected-error {{pointer to WebAssembly reference type is not allowed}} - TemplatedStruct<__externref_t &> ts3; // expected-error {{reference to WebAssembly reference type is not allowed}} - TemplatedStruct<__externref_t[0]> ts4; // expected-error {{array has sizeless element type '__externref_t'}} - - auto auto_ref = ref; - - auto fn1 = [](__externref_t x) { return x; }; - auto fn2 = [](__externref_t *x) { return x; }; // expected-error {{pointer to WebAssembly reference type is not allowed}} - auto fn3 = [](__externref_t &x) { return x; }; // expected-error {{reference to WebAssembly reference type is not allowed}} - auto fn4 = [](__externref_t x[0]) { return x; }; // expected-error {{array has sizeless element type '__externref_t'}} - auto fn5 = [&auto_ref](void) { return true; }; // expected-error {{cannot capture WebAssembly reference}} - auto fn6 = [auto_ref](void) { return true; }; // expected-error {{cannot capture WebAssembly reference}} - auto fn7 = [&](void) { auto_ref; return true; }; // expected-error {{cannot capture WebAssembly reference}} - auto fn8 = [=](void) { auto_ref; return true; }; // expected-error {{cannot capture WebAssembly reference}} - - alignof(__externref_t); // expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - alignof(ref); // expected-warning {{'alignof' applied to an expression is a GNU extension}} expected-error {{invalid application of 'alignof' to sizeless type '__externref_t'}} - alignof(__externref_t[0]); // expected-error {{array has sizeless element type '__externref_t'}} - - throw ref; // expected-error {{cannot throw object of sizeless type '__externref_t'}} - throw &ref; // expected-error {{cannot take address of WebAssembly reference}} - - try { - } catch (__externref_t) { // expected-error {{cannot catch sizeless type '__externref_t'}} - } - try { - } catch (__externref_t *) { // expected-error {{pointer to WebAssembly reference type is not allowed}} - } - try { - } catch (__externref_t &) { // expected-error {{reference to WebAssembly reference type is not allowed}} - } - try { - } catch (__externref_t[0]) { // expected-error {{array has sizeless element type '__externref_t'}} - } - - new __externref_t; // expected-error {{allocation of sizeless type '__externref_t'}} - new __externref_t[0]; // expected-error {{allocation of sizeless type '__externref_t'}} - - delete ref; // expected-error {{cannot delete expression of type '__externref_t'}} -} diff --git a/llvm/include/llvm/CodeGen/WasmAddressSpaces.h b/llvm/include/llvm/CodeGen/WasmAddressSpaces.h new file mode 100644 --- /dev/null +++ b/llvm/include/llvm/CodeGen/WasmAddressSpaces.h @@ -0,0 +1,43 @@ +//===--- llvm/CodeGen/WasmAddressSpaces.h -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Address Spaces for WebAssembly Type Handling +// +//===----------------------------------------------------------------------===// + +namespace llvm { + +namespace WebAssembly { + +enum WasmAddressSpace : unsigned { + // Default address space, for pointers to linear memory (stack, heap, data). + WASM_ADDRESS_SPACE_DEFAULT = 0, + // A non-integral address space for pointers to named objects outside of + // linear memory: WebAssembly globals or WebAssembly locals. Loads and stores + // to these pointers are lowered to global.get / global.set or local.get / + // local.set, as appropriate. + WASM_ADDRESS_SPACE_VAR = 1, + // A non-integral address space for externref values + WASM_ADDRESS_SPACE_EXTERNREF = 10, + // A non-integral address space for funcref values + WASM_ADDRESS_SPACE_FUNCREF = 20, +}; + +inline bool isDefaultAddressSpace(unsigned AS) { + return AS == WASM_ADDRESS_SPACE_DEFAULT; +} +inline bool isWasmVarAddressSpace(unsigned AS) { + return AS == WASM_ADDRESS_SPACE_VAR; +} +inline bool isValidAddressSpace(unsigned AS) { + return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS); +} + +} // namespace WebAssembly + +} // namespace llvm diff --git a/llvm/include/llvm/IR/Type.h b/llvm/include/llvm/IR/Type.h --- a/llvm/include/llvm/IR/Type.h +++ b/llvm/include/llvm/IR/Type.h @@ -20,6 +20,7 @@ #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/TypeSize.h" +#include "llvm/CodeGen/WasmAddressSpaces.h" #include #include #include @@ -215,6 +216,21 @@ return getTypeID() == ScalableVectorTyID || isScalableTargetExtTy(); } + /// Return true if this is a WebAssembly Reference Type. + bool isWebAssemblyReferenceType() const { return isWebAssemblyExternrefType() || isWebAssemblyFuncrefType(); } + + /// Return true if this is a WebAssembly Externref Type. + bool isWebAssemblyExternrefType() const { + return getPointerAddressSpace() == + WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF; + } + + /// Return true if this is a WebAssembly Funcref Type. + bool isWebAssemblyFuncrefType() const { + return getPointerAddressSpace() == + WebAssembly::WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF; + } + /// Return true if this is a FP type or a vector of FP. bool isFPOrFPVectorTy() const { return getScalarType()->isFloatingPointTy(); } diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.h @@ -27,43 +27,6 @@ namespace WebAssembly { -enum WasmAddressSpace : unsigned { - // Default address space, for pointers to linear memory (stack, heap, data). - WASM_ADDRESS_SPACE_DEFAULT = 0, - // A non-integral address space for pointers to named objects outside of - // linear memory: WebAssembly globals or WebAssembly locals. Loads and stores - // to these pointers are lowered to global.get / global.set or local.get / - // local.set, as appropriate. - WASM_ADDRESS_SPACE_VAR = 1, - // A non-integral address space for externref values - WASM_ADDRESS_SPACE_EXTERNREF = 10, - // A non-integral address space for funcref values - WASM_ADDRESS_SPACE_FUNCREF = 20, -}; - -inline bool isDefaultAddressSpace(unsigned AS) { - return AS == WASM_ADDRESS_SPACE_DEFAULT; -} -inline bool isWasmVarAddressSpace(unsigned AS) { - return AS == WASM_ADDRESS_SPACE_VAR; -} -inline bool isValidAddressSpace(unsigned AS) { - return isDefaultAddressSpace(AS) || isWasmVarAddressSpace(AS); -} -inline bool isFuncrefType(const Type *Ty) { - return isa(Ty) && - Ty->getPointerAddressSpace() == - WasmAddressSpace::WASM_ADDRESS_SPACE_FUNCREF; -} -inline bool isExternrefType(const Type *Ty) { - return isa(Ty) && - Ty->getPointerAddressSpace() == - WasmAddressSpace::WASM_ADDRESS_SPACE_EXTERNREF; -} -inline bool isRefType(const Type *Ty) { - return isFuncrefType(Ty) || isExternrefType(Ty); -} - // Convert StringRef to ValType / HealType / BlockType MVT parseMVT(StringRef Type); diff --git a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp --- a/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp +++ b/llvm/lib/Target/WebAssembly/Utils/WebAssemblyTypeUtilities.cpp @@ -78,12 +78,12 @@ wasm::ValType ValTy; bool IsTable = false; if (GlobalVT->isArrayTy() && - WebAssembly::isRefType(GlobalVT->getArrayElementType())) { + GlobalVT->getArrayElementType()->isWebAssemblyReferenceType()) { IsTable = true; const Type *ElTy = GlobalVT->getArrayElementType(); - if (WebAssembly::isExternrefType(ElTy)) + if (ElTy->isWebAssemblyExternrefType()) ValTy = wasm::ValType::EXTERNREF; - else if (WebAssembly::isFuncrefType(ElTy)) + else if (ElTy->isWebAssemblyFuncrefType()) ValTy = wasm::ValType::FUNCREF; else report_fatal_error("unhandled reference type"); diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyISelLowering.cpp @@ -1203,7 +1203,7 @@ // Lastly, if this is a call to a funcref we need to add an instruction // table.set to the chain and transform the call. if (CLI.CB && - WebAssembly::isFuncrefType(CLI.CB->getCalledOperand()->getType())) { + CLI.CB->getCalledOperand()->getType()->isWebAssemblyFuncrefType()) { // In the absence of function references proposal where a funcref call is // lowered to call_ref, using reference types we generate a table.set to set // the funcref to a special table used solely for this purpose, followed by diff --git a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp --- a/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp +++ b/llvm/lib/Target/WebAssembly/WebAssemblyLowerRefTypesIntPtrConv.cpp @@ -62,8 +62,9 @@ for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) { PtrToIntInst *PTI = dyn_cast(&*I); IntToPtrInst *ITP = dyn_cast(&*I); - if (!(PTI && WebAssembly::isRefType(PTI->getPointerOperand()->getType())) && - !(ITP && WebAssembly::isRefType(ITP->getDestTy()))) + if (!(PTI && + PTI->getPointerOperand()->getType()->isWebAssemblyReferenceType()) && + !(ITP && ITP->getDestTy()->isWebAssemblyReferenceType())) continue; UndefValue *U = UndefValue::get(I->getType());