Author: Paulo Matos Date: 2023-03-17T18:31:44+01:00 New Revision: 8d0c889752121e62e7258570c592b905f544d36f
URL: https://github.com/llvm/llvm-project/commit/8d0c889752121e62e7258570c592b905f544d36f DIFF: https://github.com/llvm/llvm-project/commit/8d0c889752121e62e7258570c592b905f544d36f.diff LOG: [clang][WebAssembly] Initial support for reference type funcref in clang This is the funcref counterpart to 890146b. We introduce a new attribute that marks a function pointer as a funcref. It also implements builtin __builtin_wasm_ref_null_func(), that returns a null funcref value. Differential Revision: https://reviews.llvm.org/D128440 Added: clang/test/CodeGen/WebAssembly/wasm-funcref.c clang/test/Parser/wasm-funcref.c clang/test/SemaCXX/wasm-funcref.cpp Modified: clang/include/clang/AST/DeclBase.h clang/include/clang/AST/Type.h clang/include/clang/Basic/AddressSpaces.h clang/include/clang/Basic/Attr.td clang/include/clang/Basic/AttrDocs.td clang/include/clang/Basic/BuiltinsWebAssembly.def clang/include/clang/Basic/DiagnosticSemaKinds.td clang/include/clang/Basic/TokenKinds.def clang/include/clang/Parse/Parser.h clang/include/clang/Sema/Sema.h clang/lib/AST/DeclBase.cpp clang/lib/AST/Type.cpp clang/lib/AST/TypePrinter.cpp clang/lib/Basic/TargetInfo.cpp clang/lib/Basic/Targets/DirectX.h clang/lib/Basic/Targets/NVPTX.h clang/lib/Basic/Targets/SPIR.h clang/lib/Basic/Targets/TCE.h clang/lib/Basic/Targets/WebAssembly.h clang/lib/Basic/Targets/X86.h clang/lib/CodeGen/CGBuiltin.cpp clang/lib/CodeGen/TargetInfo.cpp clang/lib/CodeGen/TargetInfo.h clang/lib/Format/FormatToken.h clang/lib/Parse/ParseDecl.cpp clang/lib/Parse/ParseTentative.cpp clang/lib/Sema/SemaChecking.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaType.cpp clang/test/Sema/wasm-refs.c clang/test/SemaTemplate/address_space-dependent.cpp Removed: ################################################################################ diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h index 7c3b755a438f1..e736f827f04b4 100644 --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -1227,6 +1227,10 @@ class alignas(8) Decl { /// have a FunctionType. const FunctionType *getFunctionType(bool BlocksToo = true) const; + // Looks through the Decl's underlying type to determine if it's a + // function pointer type. + bool isFunctionPointerType() const; + private: void setAttrsImpl(const AttrVec& Attrs, ASTContext &Ctx); void setDeclContextsImpl(DeclContext *SemaDC, DeclContext *LexicalDC, diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 95a5df8699afb..9ecc29bd38fd1 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -4934,6 +4934,8 @@ class AttributedType : public Type, public llvm::FoldingSetNode { bool isMSTypeSpec() const; + bool isWebAssemblyFuncrefSpec() const; + bool isCallingConv() const; std::optional<NullabilityKind> getImmediateNullability() const; diff --git a/clang/include/clang/Basic/AddressSpaces.h b/clang/include/clang/Basic/AddressSpaces.h index 2f2c5d5826bc3..7b723d508fff1 100644 --- a/clang/include/clang/Basic/AddressSpaces.h +++ b/clang/include/clang/Basic/AddressSpaces.h @@ -59,6 +59,9 @@ enum class LangAS : unsigned { // HLSL specific address spaces. hlsl_groupshared, + // Wasm specific address spaces. + wasm_funcref, + // This denotes the count of language-specific address spaces and also // the offset added to the target-specific address spaces, which are usually // specified by address space attributes __attribute__(address_space(n))). diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 89a62e83444a3..6c55465926bf7 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -168,6 +168,12 @@ def FunctionLike : SubsetSubject<DeclBase, [{S->getFunctionType(false) != nullptr}], "functions, function pointers">; +// Function Pointer is a stricter version of FunctionLike that only allows function +// pointers. +def FunctionPointer : SubsetSubject<DeclBase, + [{S->isFunctionPointerType()}], + "functions pointers">; + def OpenCLKernelFunction : SubsetSubject<Function, [{S->hasAttr<OpenCLKernelAttr>()}], "kernel functions">; @@ -4131,6 +4137,13 @@ def FunctionReturnThunks : InheritableAttr, let Subjects = SubjectList<[Function]>; let Documentation = [FunctionReturnThunksDocs]; } + +def WebAssemblyFuncref : TypeAttr, TargetSpecificAttr<TargetWebAssembly> { + let Spellings = [Keyword<"__funcref">]; + let Documentation = [WebAssemblyExportNameDocs]; + let Subjects = SubjectList<[FunctionPointer], ErrorDiag>; +} + def ReadOnlyPlacement : InheritableAttr { let Spellings = [Clang<"enforce_read_only_placement">]; let Subjects = SubjectList<[Record]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index e085e2735b208..1bb9e691725d3 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6938,3 +6938,12 @@ def ReadOnlyPlacementDocs : Documentation { ``enforce_read_only_placement`` attribute. }]; } + +def WebAssemblyFuncrefDocs : Documentation { + let Category = DocCatType; + let Content = [{ +Clang supports the ``__funcref`` attribute for the WebAssembly target. +This attribute may be attached to a function pointer type, where it modifies +its underlying representation to be a WebAssembly ``funcref``. + }]; +} diff --git a/clang/include/clang/Basic/BuiltinsWebAssembly.def b/clang/include/clang/Basic/BuiltinsWebAssembly.def index d7e588c3f4d31..ddd8bc92130af 100644 --- a/clang/include/clang/Basic/BuiltinsWebAssembly.def +++ b/clang/include/clang/Basic/BuiltinsWebAssembly.def @@ -191,8 +191,15 @@ TARGET_BUILTIN(__builtin_wasm_relaxed_dot_i8x16_i7x16_add_s_i32x4, "V4iV16ScV16S TARGET_BUILTIN(__builtin_wasm_relaxed_dot_bf16x8_add_f32_f32x4, "V4fV8UsV8UsV4f", "nc", "relaxed-simd") // Reference Types builtins +// Some builtins are custom type-checked - see 't' as part of the third argument, +// in which case the argument spec (second argument) is unused. TARGET_BUILTIN(__builtin_wasm_ref_null_extern, "i", "nct", "reference-types") +// A funcref represented as a function pointer with the funcref attribute +// attached to the type, therefore SemaChecking will check for the right +// return type. +TARGET_BUILTIN(__builtin_wasm_ref_null_func, "i", "nct", "reference-types") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 81e3f5e2d0623..bbab5752c9bfe 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -7385,6 +7385,8 @@ def err_attribute_arm_builtin_alias : Error< "'__clang_arm_builtin_alias' attribute can only be applied to an ARM builtin">; def err_attribute_arm_mve_polymorphism : Error< "'__clang_arm_mve_strict_polymorphism' attribute can only be applied to an MVE/NEON vector type">; +def err_attribute_webassembly_funcref : Error< + "'__funcref' attribute can only be applied to a function pointer type">; def warn_setter_getter_impl_required : Warning< "property %0 requires method %1 to be defined - " @@ -11798,4 +11800,6 @@ def err_wasm_reference_pr : Error< "%select{pointer|reference}0 to WebAssembly reference type is not allowed">; def err_wasm_ca_reference : Error< "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">; } // end of sema component. diff --git a/clang/include/clang/Basic/TokenKinds.def b/clang/include/clang/Basic/TokenKinds.def index fd2b4972c43ec..28862c81e9ee1 100644 --- a/clang/include/clang/Basic/TokenKinds.def +++ b/clang/include/clang/Basic/TokenKinds.def @@ -680,6 +680,9 @@ KEYWORD(_Nullable , KEYALL) KEYWORD(_Nullable_result , KEYALL) KEYWORD(_Null_unspecified , KEYALL) +// WebAssembly Type Extension +KEYWORD(__funcref , KEYALL) + // Microsoft extensions which should be disabled in strict conformance mode KEYWORD(__ptr64 , KEYMS) KEYWORD(__ptr32 , KEYMS) diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 65111b1ac6b36..2174852a94509 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2930,6 +2930,7 @@ class Parser : public CodeCompletionHandler { SourceLocation AttrNameLoc, ParsedAttributes &Attrs); void ParseMicrosoftTypeAttributes(ParsedAttributes &attrs); + void ParseWebAssemblyFuncrefTypeAttribute(ParsedAttributes &Attrs); void DiagnoseAndSkipExtendedMicrosoftTypeAttributes(); SourceLocation SkipExtendedMicrosoftTypeAttributes(); void ParseMicrosoftInheritanceClassAttributes(ParsedAttributes &attrs); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 2c3ffe0533a71..7ff10a9d52e56 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -13594,6 +13594,7 @@ class Sema final { // WebAssembly builtin handling. bool BuiltinWasmRefNullExtern(CallExpr *TheCall); + bool BuiltinWasmRefNullFunc(CallExpr *TheCall); public: enum FormatStringType { diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index c94fc602155bd..f49945f434193 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1048,6 +1048,18 @@ const FunctionType *Decl::getFunctionType(bool BlocksToo) const { return Ty->getAs<FunctionType>(); } +bool Decl::isFunctionPointerType() const { + QualType Ty; + if (const auto *D = dyn_cast<ValueDecl>(this)) + Ty = D->getType(); + else if (const auto *D = dyn_cast<TypedefNameDecl>(this)) + Ty = D->getUnderlyingType(); + else + return false; + + return Ty.getCanonicalType()->isFunctionPointerType(); +} + DeclContext *Decl::getNonTransparentDeclContext() { assert(getDeclContext()); return getDeclContext()->getNonTransparentContext(); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 71a098c254244..464939427a770 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -3674,6 +3674,10 @@ bool AttributedType::isMSTypeSpec() const { llvm_unreachable("invalid attr kind"); } +bool AttributedType::isWebAssemblyFuncrefSpec() const { + return getAttrKind() == attr::WebAssemblyFuncref; +} + bool AttributedType::isCallingConv() const { // FIXME: Generate this with TableGen. switch (getAttrKind()) { diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp index 4bd45768395a0..02a354da56a42 100644 --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -1651,6 +1651,9 @@ void TypePrinter::printAttributedBefore(const AttributedType *T, spaceBeforePlaceHolder(OS); } + if (T->isWebAssemblyFuncrefSpec()) + OS << "__funcref"; + // Print nullability type specifiers. if (T->getImmediateNullability()) { if (T->getAttrKind() == attr::TypeNonNull) @@ -1684,8 +1687,8 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, // Some attributes are printed as qualifiers before the type, so we have // nothing left to do. - if (T->getAttrKind() == attr::ObjCKindOf || - T->isMSTypeSpec() || T->getImmediateNullability()) + if (T->getAttrKind() == attr::ObjCKindOf || T->isMSTypeSpec() || + T->getImmediateNullability() || T->isWebAssemblyFuncrefSpec()) return; // Don't print the inert __unsafe_unretained attribute at all. @@ -1757,6 +1760,7 @@ void TypePrinter::printAttributedAfter(const AttributedType *T, case attr::AddressSpace: case attr::CmseNSCall: case attr::AnnotateType: + case attr::WebAssemblyFuncref: llvm_unreachable("This attribute should have been handled already"); case attr::NSReturnsRetained: @@ -2259,6 +2263,8 @@ std::string Qualifiers::getAddrSpaceAsString(LangAS AS) { return "__uptr __ptr32"; case LangAS::ptr64: return "__ptr64"; + case LangAS::wasm_funcref: + return "__funcref"; case LangAS::hlsl_groupshared: return "groupshared"; default: diff --git a/clang/lib/Basic/TargetInfo.cpp b/clang/lib/Basic/TargetInfo.cpp index 07af6c07031bd..e168d45020e77 100644 --- a/clang/lib/Basic/TargetInfo.cpp +++ b/clang/lib/Basic/TargetInfo.cpp @@ -47,6 +47,7 @@ static const LangASMap FakeAddrSpaceMap = { 11, // ptr32_uptr 12, // ptr64 13, // hlsl_groupshared + 20, // wasm_funcref }; // TargetInfo Constructor. diff --git a/clang/lib/Basic/Targets/DirectX.h b/clang/lib/Basic/Targets/DirectX.h index 2fab674d8e7bb..cb8fad2a29066 100644 --- a/clang/lib/Basic/Targets/DirectX.h +++ b/clang/lib/Basic/Targets/DirectX.h @@ -42,6 +42,9 @@ static const unsigned DirectXAddrSpaceMap[] = { 0, // ptr32_uptr 0, // ptr64 3, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; class LLVM_LIBRARY_VISIBILITY DirectXTargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/NVPTX.h b/clang/lib/Basic/Targets/NVPTX.h index 51de753665706..47f61f249ace1 100644 --- a/clang/lib/Basic/Targets/NVPTX.h +++ b/clang/lib/Basic/Targets/NVPTX.h @@ -45,6 +45,9 @@ static const unsigned NVPTXAddrSpaceMap[] = { 0, // ptr32_uptr 0, // ptr64 0, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; /// The DWARF address class. Taken from diff --git a/clang/lib/Basic/Targets/SPIR.h b/clang/lib/Basic/Targets/SPIR.h index 0a6ab9ac6b9aa..5913719858346 100644 --- a/clang/lib/Basic/Targets/SPIR.h +++ b/clang/lib/Basic/Targets/SPIR.h @@ -46,6 +46,9 @@ static const unsigned SPIRDefIsPrivMap[] = { 0, // ptr32_uptr 0, // ptr64 0, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; // Used by both the SPIR and SPIR-V targets. @@ -76,6 +79,9 @@ static const unsigned SPIRDefIsGenMap[] = { 0, // ptr32_uptr 0, // ptr64 0, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; // Base class for SPIR and SPIR-V target info. diff --git a/clang/lib/Basic/Targets/TCE.h b/clang/lib/Basic/Targets/TCE.h index 5ff379a771ac1..202554a336ab5 100644 --- a/clang/lib/Basic/Targets/TCE.h +++ b/clang/lib/Basic/Targets/TCE.h @@ -51,6 +51,9 @@ static const unsigned TCEOpenCLAddrSpaceMap[] = { 0, // ptr32_uptr 0, // ptr64 0, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; class LLVM_LIBRARY_VISIBILITY TCETargetInfo : public TargetInfo { diff --git a/clang/lib/Basic/Targets/WebAssembly.h b/clang/lib/Basic/Targets/WebAssembly.h index aeebc840a8ecd..81e3d2b648419 100644 --- a/clang/lib/Basic/Targets/WebAssembly.h +++ b/clang/lib/Basic/Targets/WebAssembly.h @@ -21,6 +21,30 @@ namespace clang { namespace targets { +static const unsigned WebAssemblyAddrSpaceMap[] = { + 0, // Default + 0, // opencl_global + 0, // opencl_local + 0, // opencl_constant + 0, // opencl_private + 0, // opencl_generic + 0, // opencl_global_device + 0, // opencl_global_host + 0, // cuda_device + 0, // cuda_constant + 0, // cuda_shared + 0, // sycl_global + 0, // sycl_global_device + 0, // sycl_global_host + 0, // sycl_local + 0, // sycl_private + 0, // ptr32_sptr + 0, // ptr32_uptr + 0, // ptr64 + 0, // hlsl_groupshared + 20, // wasm_funcref +}; + class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { enum SIMDEnum { @@ -45,6 +69,7 @@ class LLVM_LIBRARY_VISIBILITY WebAssemblyTargetInfo : public TargetInfo { public: explicit WebAssemblyTargetInfo(const llvm::Triple &T, const TargetOptions &) : TargetInfo(T) { + AddrSpaceMap = &WebAssemblyAddrSpaceMap; NoAsmVariants = true; SuitableAlign = 128; LargeArrayMinWidth = 128; diff --git a/clang/lib/Basic/Targets/X86.h b/clang/lib/Basic/Targets/X86.h index ef41195a5c096..50b9eb444b6d7 100644 --- a/clang/lib/Basic/Targets/X86.h +++ b/clang/lib/Basic/Targets/X86.h @@ -46,6 +46,9 @@ static const unsigned X86AddrSpaceMap[] = { 271, // ptr32_uptr 272, // ptr64 0, // hlsl_groupshared + // Wasm address space values for this target are dummy values, + // as it is only enabled for Wasm targets. + 20, // wasm_funcref }; // X86 target abstract base class; x86-32 and x86-64 are very close, so diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index 572d742f36a98..6381d68c161c6 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -18955,6 +18955,10 @@ Value *CodeGenFunction::EmitWebAssemblyBuiltinExpr(unsigned BuiltinID, Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_extern); return Builder.CreateCall(Callee); } + case WebAssembly::BI__builtin_wasm_ref_null_func: { + Function *Callee = CGM.getIntrinsic(Intrinsic::wasm_ref_null_func); + return Builder.CreateCall(Callee); + } case WebAssembly::BI__builtin_wasm_swizzle_i8x16: { Value *Src = EmitScalarExpr(E->getArg(0)); Value *Indices = EmitScalarExpr(E->getArg(1)); diff --git a/clang/lib/CodeGen/TargetInfo.cpp b/clang/lib/CodeGen/TargetInfo.cpp index 2d3d5368ace2b..40cc2da46bd05 100644 --- a/clang/lib/CodeGen/TargetInfo.cpp +++ b/clang/lib/CodeGen/TargetInfo.cpp @@ -900,6 +900,10 @@ class WebAssemblyTargetCodeGenInfo final : public TargetCodeGenInfo { virtual llvm::Type *getWasmExternrefReferenceType() const override { return llvm::Type::getWasm_ExternrefTy(getABIInfo().getVMContext()); } + /// Return the WebAssembly funcref reference type. + virtual llvm::Type *getWasmFuncrefReferenceType() const override { + return llvm::Type::getWasm_FuncrefTy(getABIInfo().getVMContext()); + } }; /// Classify argument of given type \p Ty. diff --git a/clang/lib/CodeGen/TargetInfo.h b/clang/lib/CodeGen/TargetInfo.h index b7e6ae85be203..3971dba70762e 100644 --- a/clang/lib/CodeGen/TargetInfo.h +++ b/clang/lib/CodeGen/TargetInfo.h @@ -365,6 +365,9 @@ class TargetCodeGenInfo { /// Return the WebAssembly externref reference type. virtual llvm::Type *getWasmExternrefReferenceType() const { return nullptr; } + /// Return the WebAssembly funcref reference type. + virtual llvm::Type *getWasmFuncrefReferenceType() const { return nullptr; } + /// Emit the device-side copy of the builtin surface type. virtual bool emitCUDADeviceBuiltinSurfaceDeviceCopy(CodeGenFunction &CGF, LValue Dst, diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 86bd85c3bbef5..5cac5776a652a 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -612,7 +612,7 @@ struct FormatToken { return isOneOf(tok::kw_const, tok::kw_restrict, tok::kw_volatile, tok::kw___attribute, tok::kw__Nonnull, tok::kw__Nullable, tok::kw__Null_unspecified, tok::kw___ptr32, tok::kw___ptr64, - TT_AttributeMacro); + tok::kw___funcref, TT_AttributeMacro); } /// Determine whether the token is a simple-type-specifier. diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index da84da04e43d0..2b5829ba0ef0e 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -852,6 +852,22 @@ void Parser::ParseMicrosoftTypeAttributes(ParsedAttributes &attrs) { } } +void Parser::ParseWebAssemblyFuncrefTypeAttribute(ParsedAttributes &attrs) { + assert(Tok.is(tok::kw___funcref)); + SourceLocation StartLoc = Tok.getLocation(); + if (!getTargetInfo().getTriple().isWasm()) { + ConsumeToken(); + Diag(StartLoc, diag::err_wasm_funcref_not_wasm); + return; + } + + IdentifierInfo *AttrName = Tok.getIdentifierInfo(); + SourceLocation AttrNameLoc = ConsumeToken(); + attrs.addNew(AttrName, AttrNameLoc, /*ScopeName=*/nullptr, + /*ScopeLoc=*/SourceLocation{}, /*Args=*/nullptr, /*numArgs=*/0, + ParsedAttr::AS_Keyword); +} + void Parser::DiagnoseAndSkipExtendedMicrosoftTypeAttributes() { SourceLocation StartLoc = Tok.getLocation(); SourceLocation EndLoc = SkipExtendedMicrosoftTypeAttributes(); @@ -3845,6 +3861,10 @@ void Parser::ParseDeclarationSpecifiers( ParseMicrosoftTypeAttributes(DS.getAttributes()); continue; + case tok::kw___funcref: + ParseWebAssemblyFuncrefTypeAttribute(DS.getAttributes()); + continue; + // Borland single token adornments. case tok::kw___pascal: ParseBorlandTypeAttributes(DS.getAttributes()); @@ -5409,7 +5429,7 @@ bool Parser::isTypeSpecifierQualifier() { case tok::kw___read_only: case tok::kw___read_write: case tok::kw___write_only: - + case tok::kw___funcref: case tok::kw_groupshared: return true; @@ -5674,6 +5694,7 @@ bool Parser::isDeclarationSpecifier( #define GENERIC_IMAGE_TYPE(ImgType, Id) case tok::kw_##ImgType##_t: #include "clang/Basic/OpenCLImageTypes.def" + case tok::kw___funcref: case tok::kw_groupshared: return true; @@ -5939,6 +5960,12 @@ void Parser::ParseTypeQualifierListOpt( continue; } goto DoneWithTypeQuals; + + case tok::kw___funcref: + ParseWebAssemblyFuncrefTypeAttribute(DS.getAttributes()); + continue; + goto DoneWithTypeQuals; + case tok::kw___pascal: if (AttrReqs & AR_VendorAttributesParsed) { ParseBorlandTypeAttributes(DS.getAttributes()); diff --git a/clang/lib/Parse/ParseTentative.cpp b/clang/lib/Parse/ParseTentative.cpp index 785749bff65a2..b26faaff7f9f3 100644 --- a/clang/lib/Parse/ParseTentative.cpp +++ b/clang/lib/Parse/ParseTentative.cpp @@ -1511,6 +1511,10 @@ Parser::isCXXDeclarationSpecifier(ImplicitTypenameContext AllowImplicitTypename, case tok::kw___kindof: return TPResult::True; + // WebAssemblyFuncref + case tok::kw___funcref: + return TPResult::True; + // Borland case tok::kw___pascal: return TPResult::True; diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index be71d862a490a..02d1cb010db50 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4733,6 +4733,8 @@ bool Sema::CheckWebAssemblyBuiltinFunctionCall(const TargetInfo &TI, switch (BuiltinID) { case WebAssembly::BI__builtin_wasm_ref_null_extern: return BuiltinWasmRefNullExtern(TheCall); + case WebAssembly::BI__builtin_wasm_ref_null_func: + return BuiltinWasmRefNullFunc(TheCall); } return false; @@ -6767,6 +6769,25 @@ bool Sema::BuiltinWasmRefNullExtern(CallExpr *TheCall) { return false; } +bool Sema::BuiltinWasmRefNullFunc(CallExpr *TheCall) { + if (TheCall->getNumArgs() != 0) { + Diag(TheCall->getBeginLoc(), diag::err_typecheck_call_too_many_args) + << 0 /*function call*/ << 0 << TheCall->getNumArgs(); + return true; + } + + // This custom type checking code ensures that the nodes are as expected + // in order to later on generate the necessary builtin. + QualType Pointee = Context.getFunctionType(Context.VoidTy, {}, {}); + QualType Type = Context.getPointerType(Pointee); + Pointee = Context.getAddrSpaceQualType(Pointee, LangAS::wasm_funcref); + Type = Context.getAttributedType(attr::WebAssemblyFuncref, Type, + Context.getPointerType(Pointee)); + TheCall->setType(Type); + + return false; +} + /// We have a call to a function like __sync_fetch_and_add, which is an /// overloaded function based on the pointer type of its first argument. /// The main BuildCallExpr routines have already promoted the types of diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 381673b88b436..64034393344f0 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -14845,7 +14845,11 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc, // OpenCL allows function arguments declared to be an array of a type // to be qualified with an address space. !(getLangOpts().OpenCL && - (T->isArrayType() || T.getAddressSpace() == LangAS::opencl_private))) { + (T->isArrayType() || T.getAddressSpace() == LangAS::opencl_private)) && + // WebAssembly allows reference types as parameters. Funcref in particular + // lives in a diff erent address space. + !(T->isFunctionPointerType() && + T.getAddressSpace() == LangAS::wasm_funcref)) { Diag(NameLoc, diag::err_arg_with_address_space); New->setInvalidDecl(); } diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp index 54ce3ec806f57..53852dd930a71 100644 --- a/clang/lib/Sema/SemaType.cpp +++ b/clang/lib/Sema/SemaType.cpp @@ -7346,6 +7346,37 @@ static bool handleMSPointerTypeQualifierAttr(TypeProcessingState &State, return false; } +static bool HandleWebAssemblyFuncrefAttr(TypeProcessingState &State, + QualType &QT, ParsedAttr &PAttr) { + assert(PAttr.getKind() == ParsedAttr::AT_WebAssemblyFuncref); + + Sema &S = State.getSema(); + Attr *A = createSimpleAttr<WebAssemblyFuncrefAttr>(S.Context, PAttr); + + std::bitset<attr::LastAttr> Attrs; + attr::Kind NewAttrKind = A->getKind(); + const auto *AT = dyn_cast<AttributedType>(QT); + while (AT) { + Attrs[AT->getAttrKind()] = true; + AT = dyn_cast<AttributedType>(AT->getModifiedType()); + } + + // You cannot specify duplicate type attributes, so if the attribute has + // already been applied, flag it. + if (Attrs[NewAttrKind]) { + S.Diag(PAttr.getLoc(), diag::warn_duplicate_attribute_exact) << PAttr; + return true; + } + + // Add address space to type based on its attributes. + LangAS ASIdx = LangAS::wasm_funcref; + QualType Pointee = QT->getPointeeType(); + Pointee = S.Context.getAddrSpaceQualType( + S.Context.removeAddrSpaceQualType(Pointee), ASIdx); + QT = State.getAttributedType(A, QT, S.Context.getPointerType(Pointee)); + return false; +} + /// Map a nullability attribute kind to a nullability kind. static NullabilityKind mapNullabilityAttrKind(ParsedAttr::Kind kind) { switch (kind) { @@ -8503,6 +8534,12 @@ static void processTypeAttrs(TypeProcessingState &state, QualType &type, attr.setUsedAsTypeAttr(); break; + case ParsedAttr::AT_WebAssemblyFuncref: { + if (!HandleWebAssemblyFuncrefAttr(state, type, attr)) + attr.setUsedAsTypeAttr(); + break; + } + MS_TYPE_ATTRS_CASELIST: if (!handleMSPointerTypeQualifierAttr(state, attr, type)) attr.setUsedAsTypeAttr(); diff --git a/clang/test/CodeGen/WebAssembly/wasm-funcref.c b/clang/test/CodeGen/WebAssembly/wasm-funcref.c new file mode 100644 index 0000000000000..f01af0db321dd --- /dev/null +++ b/clang/test/CodeGen/WebAssembly/wasm-funcref.c @@ -0,0 +1,99 @@ +// NOTE: Assertions have been autogenerated by utils/update_cc_test_checks.py +// RUN: %clang_cc1 -triple wasm32 -target-feature +reference-types -o - -emit-llvm %s | FileCheck %s + +typedef void (*__funcref funcref_t)(); +typedef int (*__funcref fn_funcref_t)(int); +typedef int (*fn_t)(int); + +// Null funcref builtin call +// CHECK-LABEL: @get_null( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func() +// CHECK-NEXT: ret ptr addrspace(20) [[TMP0]] +// +funcref_t get_null() { + return __builtin_wasm_ref_null_func(); +} + +// Call to null funcref builtin but requires cast since +// default return value for builtin is a funcref with function type () -> (). +// CHECK-LABEL: @get_null_ii( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[TMP0:%.*]] = call ptr addrspace(20) @llvm.wasm.ref.null.func() +// CHECK-NEXT: ret ptr addrspace(20) [[TMP0]] +// +fn_funcref_t get_null_ii() { + return (fn_funcref_t) __builtin_wasm_ref_null_func(); +} + +// Identity function for funcref. +// CHECK-LABEL: @identity( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4 +// CHECK-NEXT: store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4 +// CHECK-NEXT: ret ptr addrspace(20) [[TMP0]] +// +funcref_t identity(funcref_t fn) { + return fn; +} + +void helper(funcref_t); + +// Pass funcref ref as an argument to a helper function. +// CHECK-LABEL: @handle( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FN_ADDR:%.*]] = alloca ptr addrspace(20), align 4 +// CHECK-NEXT: store ptr addrspace(20) [[FN:%.*]], ptr [[FN_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(20), ptr [[FN_ADDR]], align 4 +// CHECK-NEXT: call void @helper(ptr addrspace(20) noundef [[TMP0]]) +// CHECK-NEXT: ret i32 0 +// +int handle(funcref_t fn) { + helper(fn); + return 0; +} + +// Return funcref from function pointer. +// CHECK-LABEL: @get_ref( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[FNPTR_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: store ptr [[FNPTR:%.*]], ptr [[FNPTR_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[FNPTR_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = addrspacecast ptr [[TMP0]] to ptr addrspace(20) +// CHECK-NEXT: ret ptr addrspace(20) [[TMP1]] +// +fn_funcref_t get_ref(fn_t fnptr) { + return (fn_funcref_t) fnptr; +} + +// Call funcref +// CHECK-LABEL: @call_fn( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[REF_ADDR:%.*]] = alloca ptr addrspace(20), align 4 +// CHECK-NEXT: [[X_ADDR:%.*]] = alloca i32, align 4 +// CHECK-NEXT: store ptr addrspace(20) [[REF:%.*]], ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: store i32 [[X:%.*]], ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr addrspace(20), ptr [[REF_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[X_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call addrspace(20) i32 [[TMP0]](i32 noundef [[TMP1]]) +// CHECK-NEXT: ret i32 [[CALL]] +// +int call_fn(fn_funcref_t ref, int x) { + return ref(x); +} + +typedef fn_funcref_t (*builtin_refnull_t)(); + +// Calling ref.null through a function pointer. +// CHECK-LABEL: @get_null_fptr( +// CHECK-NEXT: entry: +// CHECK-NEXT: [[REFNULL_ADDR:%.*]] = alloca ptr, align 4 +// CHECK-NEXT: store ptr [[REFNULL:%.*]], ptr [[REFNULL_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load ptr, ptr [[REFNULL_ADDR]], align 4 +// CHECK-NEXT: [[CALL:%.*]] = call ptr addrspace(20) [[TMP0]]() +// CHECK-NEXT: ret ptr addrspace(20) [[CALL]] +// +fn_funcref_t get_null_fptr(builtin_refnull_t refnull) { + return refnull(); +} diff --git a/clang/test/Parser/wasm-funcref.c b/clang/test/Parser/wasm-funcref.c new file mode 100644 index 0000000000000..5f5b1f60dc46b --- /dev/null +++ b/clang/test/Parser/wasm-funcref.c @@ -0,0 +1,9 @@ +// RUN: %clang_cc1 -triple powerpc-linux-gnu -fsyntax-only -verify %s + +// Test that we trigger an error at parse time if using keyword funcref +// while not using a wasm triple. +typedef void (*__funcref funcref_t)(); // expected-error {{invalid use of '__funcref' keyword outside the WebAssembly triple}} +typedef int (*__funcref fn_funcref_t)(int);// expected-error {{invalid use of '__funcref' keyword outside the WebAssembly triple}} +typedef int (*fn_t)(int); + +static fn_funcref_t nullFuncref = 0; diff --git a/clang/test/Sema/wasm-refs.c b/clang/test/Sema/wasm-refs.c index 376d539556e6c..10aefc9aa04ee 100644 --- a/clang/test/Sema/wasm-refs.c +++ b/clang/test/Sema/wasm-refs.c @@ -46,6 +46,8 @@ __externref_t *illegal_return_1(); // expected-error {{pointer to WebAssembly __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}} @@ -67,5 +69,7 @@ __externref_t func(__externref_t ref) { _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-funcref.cpp b/clang/test/SemaCXX/wasm-funcref.cpp new file mode 100644 index 0000000000000..364565e9e803c --- /dev/null +++ b/clang/test/SemaCXX/wasm-funcref.cpp @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -std=c++11 -fcxx-exceptions -fexceptions -fsyntax-only -verify -triple wasm32 -Wno-unused-value -target-feature +reference-types %s + +// Testing that funcrefs work on template aliases +// expected-no-diagnostics + +using IntIntFuncref = int(*)(int) __funcref; +using DoubleQual = IntIntFuncref __funcref; + +int get(int); + +IntIntFuncref getFuncref() { + return get; +} diff --git a/clang/test/SemaTemplate/address_space-dependent.cpp b/clang/test/SemaTemplate/address_space-dependent.cpp index 3436cac4d4cb0..c8cc67ef45211 100644 --- a/clang/test/SemaTemplate/address_space-dependent.cpp +++ b/clang/test/SemaTemplate/address_space-dependent.cpp @@ -43,7 +43,7 @@ void neg() { template <long int I> void tooBig() { - __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388587)}} + __attribute__((address_space(I))) int *bounds; // expected-error {{address space is larger than the maximum supported (8388586)}} } template <long int I> @@ -101,7 +101,7 @@ int main() { car<1, 2, 3>(); // expected-note {{in instantiation of function template specialization 'car<1, 2, 3>' requested here}} HasASTemplateFields<1> HASTF; neg<-1>(); // expected-note {{in instantiation of function template specialization 'neg<-1>' requested here}} - correct<0x7FFFEA>(); + correct<0x7FFFE9>(); tooBig<8388650>(); // expected-note {{in instantiation of function template specialization 'tooBig<8388650L>' requested here}} __attribute__((address_space(1))) char *x; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits