diff --git a/clang/include/clang/AST/Expr.h b/clang/include/clang/AST/Expr.h --- a/clang/include/clang/AST/Expr.h +++ b/clang/include/clang/AST/Expr.h @@ -2665,7 +2665,7 @@ : Expr(ArraySubscriptExprClass, t, VK, OK) { SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; - ArrayOrMatrixSubscriptExprBits.RBracketLoc = rbracketloc; + AMTSubscriptExprBits.RBracketLoc = rbracketloc; setDependence(computeDependence(this)); } @@ -2702,10 +2702,10 @@ SourceLocation getEndLoc() const { return getRBracketLoc(); } SourceLocation getRBracketLoc() const { - return ArrayOrMatrixSubscriptExprBits.RBracketLoc; + return AMTSubscriptExprBits.RBracketLoc; } void setRBracketLoc(SourceLocation L) { - ArrayOrMatrixSubscriptExprBits.RBracketLoc = L; + AMTSubscriptExprBits.RBracketLoc = L; } SourceLocation getExprLoc() const LLVM_READONLY { @@ -2743,7 +2743,7 @@ SubExprs[BASE] = Base; SubExprs[ROW_IDX] = RowIdx; SubExprs[COLUMN_IDX] = ColumnIdx; - ArrayOrMatrixSubscriptExprBits.RBracketLoc = RBracketLoc; + AMTSubscriptExprBits.RBracketLoc = RBracketLoc; setDependence(computeDependence(this)); } @@ -2784,10 +2784,10 @@ } SourceLocation getRBracketLoc() const { - return ArrayOrMatrixSubscriptExprBits.RBracketLoc; + return AMTSubscriptExprBits.RBracketLoc; } void setRBracketLoc(SourceLocation L) { - ArrayOrMatrixSubscriptExprBits.RBracketLoc = L; + AMTSubscriptExprBits.RBracketLoc = L; } static bool classof(const Stmt *T) { diff --git a/clang/include/clang/AST/Stmt.h b/clang/include/clang/AST/Stmt.h --- a/clang/include/clang/AST/Stmt.h +++ b/clang/include/clang/AST/Stmt.h @@ -453,7 +453,7 @@ unsigned IsType : 1; // true if operand is a type, false if an expression. }; - class ArrayOrMatrixSubscriptExprBitfields { + class AMTSubscriptExprBitfields { friend class ArraySubscriptExpr; friend class MatrixSubscriptExpr; @@ -1042,7 +1042,7 @@ CharacterLiteralBitfields CharacterLiteralBits; UnaryOperatorBitfields UnaryOperatorBits; UnaryExprOrTypeTraitExprBitfields UnaryExprOrTypeTraitExprBits; - ArrayOrMatrixSubscriptExprBitfields ArrayOrMatrixSubscriptExprBits; + AMTSubscriptExprBitfields AMTSubscriptExprBits; CallExprBitfields CallExprBits; MemberExprBitfields MemberExprBits; CastExprBitfields CastExprBits; 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 @@ -905,6 +905,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. @@ -2029,10 +2038,14 @@ /// Returns true for SVE scalable vector types. bool isSVESizelessBuiltinType() 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 @@ -11787,4 +11787,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}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/Basic/Specifiers.h b/clang/include/clang/Basic/Specifiers.h --- a/clang/include/clang/Basic/Specifiers.h +++ b/clang/include/clang/Basic/Specifiers.h @@ -154,7 +154,10 @@ OK_ObjCSubscript, /// A matrix component is a single element of a matrix. - OK_MatrixComponent + OK_MatrixComponent, + + /// A table component is a single element of a WebAssembly table. + OK_TableComponent, }; /// The reason why a DeclRefExpr does not constitute an odr-use. 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 @@ -13529,6 +13529,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/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1651,7 +1651,7 @@ /// An MatrixSubscriptExpr record. EXPR_MATRIX_SUBSCRIPT, - + /// A CallExpr record. EXPR_CALL, diff --git a/clang/include/clang/Serialization/TypeBitCodes.def b/clang/include/clang/Serialization/TypeBitCodes.def --- a/clang/include/clang/Serialization/TypeBitCodes.def +++ b/clang/include/clang/Serialization/TypeBitCodes.def @@ -64,5 +64,4 @@ TYPE_BIT_CODE(DependentSizedMatrix, DEPENDENT_SIZE_MATRIX, 53) TYPE_BIT_CODE(Using, USING, 54) TYPE_BIT_CODE(BTFTagAttributed, BTFTAG_ATTRIBUTED, 55) - #undef TYPE_BIT_CODE diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -176,6 +176,9 @@ case OK_MatrixComponent: OS << " matrixcomponent"; break; + case OK_TableComponent: + OS << " tablecomponent"; + break; } } } 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 @@ -2346,16 +2346,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 { @@ -2594,6 +2600,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 @@ -19196,6 +19196,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" @@ -153,6 +155,7 @@ Builder.CreateBitCast(Result.getPointer(), VectorTy->getPointerTo()), VectorTy, Result.getAlignment()); } + return Result; } @@ -1936,7 +1939,7 @@ if (LV.getType()->isConstantMatrixType()) return EmitLoadOfMatrixLValue(LV, Loc, *this); - + // Everything needs a load. return RValue::get(EmitLoadOfScalar(LV, Loc)); } @@ -1965,6 +1968,7 @@ llvm::MatrixBuilder MB(Builder); MB.CreateIndexAssumption(Idx, MatTy->getNumElementsFlattened()); } + llvm::LoadInst *Load = Builder.CreateLoad(LV.getMatrixAddress(), LV.isVolatileQualified()); return RValue::get(Builder.CreateExtractElement(Load, Idx, "matrixext")); diff --git a/clang/lib/CodeGen/CGValue.h b/clang/lib/CodeGen/CGValue.h --- a/clang/lib/CodeGen/CGValue.h +++ b/clang/lib/CodeGen/CGValue.h @@ -175,7 +175,8 @@ BitField, // This is a bitfield l-value, use getBitfield*. ExtVectorElt, // This is an extended vector subset, use getExtVectorComp GlobalReg, // This is a register l-value, use getGlobalReg() - MatrixElt // This is a matrix element, use getVector* + MatrixElt, // This is a matrix element, use getVector* + TableElt, // This is a table element, use getTable* } LVType; llvm::Value *V; @@ -268,6 +269,7 @@ bool isExtVectorElt() const { return LVType == ExtVectorElt; } bool isGlobalReg() const { return LVType == GlobalReg; } bool isMatrixElt() const { return LVType == MatrixElt; } + bool isTableElt() const { return LVType == TableElt; } bool isVolatileQualified() const { return Quals.hasVolatile(); } bool isRestrictQualified() const { return Quals.hasRestrict(); } @@ -373,6 +375,19 @@ return VectorIdx; } + Address getTableAddress() const { + // FIXME: maybe this should not be possible. + return Address(getTablePointer(), ElementType, getAlignment()); + } + llvm::Value *getTablePointer() const { + assert(isTableElt()); + return V; + } + llvm::Value *getTableIdx() const { + assert(isTableElt()); + return VectorIdx; + } + // extended vector elements. Address getExtVectorAddress() const { return Address(getExtVectorPointer(), ElementType, getAlignment()); @@ -482,6 +497,19 @@ return R; } + static LValue MakeTableElt(Address tabAddress, llvm::Value *Idx, + QualType type, LValueBaseInfo BaseInfo, + TBAAAccessInfo TBAAInfo) { + LValue R; + R.LVType = TableElt; + R.V = tabAddress.getPointer(); + R.ElementType = tabAddress.getElementType(); + R.VectorIdx = Idx; + R.Initialize(type, type.getQualifiers(), tabAddress.getAlignment(), + BaseInfo, TBAAInfo); + return R; + } + RValue asAggregateRValue(CodeGenFunction &CGF) const { return RValue::getAggregate(getAddress(CGF), isVolatileQualified()); } 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 @@ -2262,6 +2262,9 @@ case OK_MatrixComponent: inappropriate = "matrix element"; break; + case OK_TableComponent: + inappropriate = "table element"; + break; case OK_ObjCProperty: inappropriate = "property expression"; break; case OK_ObjCSubscript: inappropriate = "container subscripting expression"; break; @@ -2736,6 +2739,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, @@ -2911,6 +2923,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 @@ -4717,6 +4717,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; @@ -12187,6 +12199,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()) @@ -15880,6 +15896,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; @@ -18114,6 +18137,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 @@ -7833,6 +7833,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); @@ -7989,6 +8001,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() @@ -8595,6 +8614,28 @@ } } + // WebAssembly tables must be static with a zero length and can't be + // declared within functions. + if (T->isWebAssemblyTableType()) { + if (getCurScope()->isFunctionScope()) { + 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()) @@ -8666,7 +8707,7 @@ } if (!NewVD->hasLocalStorage() && T->isSizelessType() && - !T->isWebAssemblyReferenceType()) { + !T.isWebAssemblyReferenceType()) { Diag(NewVD->getLocation(), diag::err_sizeless_nonlocal) << T; NewVD->setInvalidDecl(); return; @@ -10664,6 +10705,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. @@ -13025,6 +13074,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 @@ -16458,6 +16458,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 @@ -119,6 +119,7 @@ /// \param[in,out] T The exception type. This will be decayed to a pointer type /// when the input is an array or a function type. bool Sema::CheckSpecifiedExceptionType(QualType &T, SourceRange Range) { + // C++11 [except.spec]p2: // A type cv T, "array of T", or "function returning T" denoted // in an exception-specification is adjusted to type T, "pointer to T", or @@ -172,6 +173,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) { @@ -179,7 +186,7 @@ << (Kind == 2 ? 1 : 0) << PointeeT << Range; return true; } - + return false; } 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 @@ -940,7 +940,7 @@ return VAK_Invalid; if (Context.getTargetInfo().getTriple().isWasm() && - Ty->isWebAssemblyReferenceType()) { + Ty.isWebAssemblyReferenceType()) { return VAK_Invalid; } @@ -4298,6 +4298,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 @@ -4413,6 +4422,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; @@ -5826,6 +5844,7 @@ if (!ResultType.hasQualifiers()) VK = VK_PRValue; } else if (!ResultType->isDependentType() && + !ResultType.isWebAssemblyReferenceType() && RequireCompleteSizedType( LLoc, ResultType, diag::err_subscript_incomplete_or_sizeless_type, BaseExpr)) @@ -7331,6 +7350,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)) @@ -8894,8 +8923,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. @@ -12676,6 +12711,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 = @@ -13594,6 +13635,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. @@ -14135,6 +14186,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(); @@ -14510,7 +14567,8 @@ AO_Property_Expansion = 2, AO_Register_Variable = 3, AO_Matrix_Element = 4, - AO_No_Error = 5 + AO_Table_Element = 5, + AO_No_Error = 6 }; } /// Diagnose invalid operand for address of operations. @@ -14681,6 +14739,9 @@ } else if (op->getObjectKind() == OK_MatrixComponent) { // The operand cannot be an element of a matrix. AddressOfError = AO_Matrix_Element; + } else if (op->getObjectKind() == OK_TableComponent) { + // The operand cannot be an element of a table. + AddressOfError = AO_Table_Element; } else if (dcl) { // C99 6.5.3.2p1 // We have an lvalue with a decl. Make sure the decl is not declared // with the register storage-class specifier. @@ -14742,11 +14803,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); @@ -15922,6 +15991,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)) { @@ -18856,7 +18932,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 @@ -6568,6 +6581,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 @@ -11976,7 +11976,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 @@ -2197,11 +2197,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. @@ -2279,11 +2287,15 @@ 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. @@ -2489,12 +2501,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(); } @@ -3002,6 +3024,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/CodeGen/WebAssembly/table.c b/clang/test/CodeGen/WebAssembly/table.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/table.c @@ -0,0 +1,10 @@ +// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s +// REQUIRES: webassembly-registered-target + +// CHECK: @table = internal addrspace(1) global [0 x ptr addrspace(10)] zeroinitializer, align 1 +static __externref_t table[0]; + +void use() { + // Ensure the table isn't discarded as unused. + table[0]; +} 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,47 @@ +// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s + +static __externref_t table[0]; + +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}} + void *a = __builtin_wasm_table_size(table); // expected-error {{incompatible integer to pointer conversion initializing 'void *' with an expression of type 'int'}} + __externref_t b = __builtin_wasm_table_size(table); // expected-error {{initializing '__externref_t' with an expression of incompatible type 'int'}} + int c = __builtin_wasm_table_size(table); +} + +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}} + void *a = __builtin_wasm_table_grow(table, ref, size); // expected-error {{incompatible integer to pointer conversion initializing 'void *' with an expression of type 'int'}} + int b = __builtin_wasm_table_grow(table, ref, size); +} + +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}} + void *a = __builtin_wasm_table_fill(table, index, ref, nelem); // expected-error {{initializing 'void *' with an expression of incompatible type 'void'}} + __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.c b/clang/test/Sema/wasm-refs.c --- a/clang/test/Sema/wasm-refs.c +++ b/clang/test/Sema/wasm-refs.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -fsyntax-only -verify -triple wasm32 -target-feature +reference-types %s +// RUN: %clang_cc1 -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. @@ -11,39 +11,45 @@ __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 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]; // expected-error {{array has sizeless element type '__externref_t'}} +static __externref_t table[0]; +static __externref_t other_table[0] = {}; 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 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 {{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 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 {{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_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}} __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, ...); @@ -54,18 +60,46 @@ (__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[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 {{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 {{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}} + illegal_argument_1(table); // expected-error {{cannot use WebAssembly table as a function parameter}} + 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; // 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}} + + table[0]; + table[0] = ref; return ref; } + +void *ret_void_ptr() { + return table; // expected-error {{cannot return a WebAssembly table}} +} 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,116 @@ +// RUN: %clang_cc1 -std=c++11 -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; +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] = {}; + +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 illegal}} + __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 illegal}} +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}} + +__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, ...); + +__externref_t func(__externref_t ref) throw(__externref_t) { // expected-error {{WebAssembly reference type not allowed in exception specification}} + &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 illegal}} + 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 illegal}} + _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 illegal}} + static __externref_t(*lt4)[0]; // expected-error {{cannot form a pointer to a WebAssembly table}} + illegal_argument_1(table); // expected-error {{no matching function for call to 'illegal_argument_1'}} + 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; // 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}} + + + table[0]; + table[0] = ref; + return ref; +} + +void *ret_void_ptr() { + throw table; // expected-error {{cannot throw a WebAssembly table}} + 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}} +} 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 \ No newline at end of file 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 @@ -206,6 +207,21 @@ /// Return true if this is a target extension type. bool isTargetExtTy() const { return getTypeID() == TargetExtTyID; } + /// 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 @@ -45,43 +45,6 @@ Multivalue = 0xffff, }; -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); -} - inline bool isRefType(wasm::ValType Type) { return Type == wasm::ValType::EXTERNREF || Type == wasm::ValType::FUNCREF; } 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 @@ -184,12 +184,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 @@ -1202,7 +1202,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());