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 @@ -3912,6 +3912,20 @@ /// because TrailingObjects cannot handle repeated types. struct ExceptionType { QualType Type; }; + /// The AArch64 SME ACLE (Arm C/C++ Language Extensions) define a number + /// of function type attributes that can be set on function types, including + /// function pointers. + enum AArch64SMETypeAttributes : unsigned { + SME_NormalFunction = 0, + SME_PStateSMEnabledMask = 1 << 0, + SME_PStateSMCompatibleMask = 1 << 1, + SME_PStateZANewMask = 1 << 2, + SME_PStateZASharedMask = 1 << 3, + SME_PStateZAPreservedMask = 1 << 4, + SME_AttributeMask = 255 // We only support maximum 8 bits because of the + // bitmask in FunctionTypeExtraBitfields. + }; + /// A simple holder for various uncommon bits which do not fit in /// FunctionTypeBitfields. Aligned to alignof(void *) to maintain the /// alignment of subsequent objects in TrailingObjects. @@ -3920,6 +3934,13 @@ /// A whole unsigned is not needed here and according to /// [implimits] 8 bits would be enough here. unsigned NumExceptionType = 0; + + /// Any AArch64 SME ACLE type attributes that need to be propagated + /// on declarations and function pointers. + unsigned AArch64SMEAttributes : 8; + + FunctionTypeExtraBitfields() + : AArch64SMEAttributes(SME_NormalFunction) {} }; protected: @@ -4100,16 +4121,20 @@ FunctionType::ExtInfo ExtInfo; bool Variadic : 1; bool HasTrailingReturn : 1; + unsigned AArch64SMEAttributes : 8; Qualifiers TypeQuals; RefQualifierKind RefQualifier = RQ_None; ExceptionSpecInfo ExceptionSpec; const ExtParameterInfo *ExtParameterInfos = nullptr; SourceLocation EllipsisLoc; - ExtProtoInfo() : Variadic(false), HasTrailingReturn(false) {} + ExtProtoInfo() + : Variadic(false), HasTrailingReturn(false), + AArch64SMEAttributes(SME_NormalFunction) {} ExtProtoInfo(CallingConv CC) - : ExtInfo(CC), Variadic(false), HasTrailingReturn(false) {} + : ExtInfo(CC), Variadic(false), HasTrailingReturn(false), + AArch64SMEAttributes(SME_NormalFunction) {} ExtProtoInfo withExceptionSpec(const ExceptionSpecInfo &ESI) { ExtProtoInfo Result(*this); @@ -4118,7 +4143,16 @@ } bool requiresFunctionProtoTypeExtraBitfields() const { - return ExceptionSpec.Type == EST_Dynamic; + return ExceptionSpec.Type == EST_Dynamic || + AArch64SMEAttributes != SME_NormalFunction; + } + + void setArmSMEAttribute(AArch64SMETypeAttributes Kind, bool Enable = true) { + if (Enable) + AArch64SMEAttributes |= Kind; + else + AArch64SMEAttributes = + (AArch64SMEAttributes ^ Kind) & AArch64SMEAttributes; } }; @@ -4245,6 +4279,7 @@ EPI.TypeQuals = getMethodQuals(); EPI.RefQualifier = getRefQualifier(); EPI.ExtParameterInfos = getExtParameterInfosOrNull(); + EPI.AArch64SMEAttributes = getAArch64SMEAttributes(); return EPI; } @@ -4426,6 +4461,14 @@ return getTrailingObjects(); } + /// Return a bitmask describing the SME attributes on the function type, see + /// AArch64SMETypeAttributes for their values. + unsigned getAArch64SMEAttributes() const { + if (!hasExtraBitfields()) + return SME_NormalFunction; + return getTrailingObjects()->AArch64SMEAttributes; + } + ExtParameterInfo getExtParameterInfo(unsigned I) const { assert(I < getNumParams() && "parameter index out of range"); if (hasExtParameterInfos()) diff --git a/clang/include/clang/AST/TypeProperties.td b/clang/include/clang/AST/TypeProperties.td --- a/clang/include/clang/AST/TypeProperties.td +++ b/clang/include/clang/AST/TypeProperties.td @@ -323,6 +323,9 @@ ? node->getExtParameterInfos() : llvm::ArrayRef() }]; } + def : Property<"AArch64SMEAttributes", UInt32> { + let Read = [{ node->getAArch64SMEAttributes() }]; + } def : Creator<[{ auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm, @@ -338,6 +341,7 @@ epi.ExceptionSpec = exceptionSpecifier; epi.ExtParameterInfos = extParameterInfo.empty() ? nullptr : extParameterInfo.data(); + epi.AArch64SMEAttributes = AArch64SMEAttributes; return ctx.getFunctionType(returnType, parameters, epi); }]>; } diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -397,6 +397,9 @@ } def TargetARM : TargetArch<["arm", "thumb", "armeb", "thumbeb"]>; def TargetAArch64 : TargetArch<["aarch64"]>; +def TargetAArch64SME : TargetArch<["aarch64"]> { + let CustomCode = [{ Target.hasFeature("sme") }]; +} def TargetAnyArm : TargetArch; def TargetAVR : TargetArch<["avr"]>; def TargetBPF : TargetArch<["bpfel", "bpfeb"]>; @@ -2384,6 +2387,42 @@ let Documentation = [AArch64VectorPcsDocs]; } +def ArmStreamingCompatible : TypeAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_streaming_compatible">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeStreamingCompatibleDocs]; +} + +def ArmStreaming : TypeAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_streaming">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeStreamingDocs]; +} + +def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_locally_streaming">]; + let Subjects = SubjectList<[Function], ErrorDiag>; + let Documentation = [ArmSmeLocallyStreamingDocs]; +} + +def ArmSharedZA : TypeAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_shared_za">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeSharedZADocs]; +} + +def ArmPreservesZA : TypeAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_preserves_za">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmePreservesZADocs]; +} + +def ArmNewZA : TypeAttr, TargetSpecificAttr { + let Spellings = [GNU<"arm_new_za">]; + let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>; + let Documentation = [ArmSmeNewZADocs]; +} + def AArch64SVEPcs: DeclOrTypeAttr { let Spellings = [Clang<"aarch64_sve_pcs">]; let Documentation = [AArch64SVEPcsDocs]; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -6314,6 +6314,92 @@ }]; } +def DocCatACLE_SME : DocumentationCategory<"ARM C Language Extensions for SME"> { + let Content = [{ +Clang implements builtins and function attributes as defined in the `Arm C +Language Extensions for SME `_. + +Only when Clang defines __ARM_FEATURE_SME, the support for this feature is +considered to be stable and complete. +}]; +} + +def ArmSmeStreamingDocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +The ``arm_streaming`` attribute is used to mark a function as being a +streaming function, meaning that it is expected to be called with +``PSTATE.SM=1`` and return with ``PSTATE.SM=1``. + +By adding this attribute, Clang may insert the appropriate ``smstart`` and +``smstop`` instructions before and after the call to guarantee that these +conditions are satisfied. + }]; +} + +def ArmSmeLocallyStreamingDocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +The ``arm_locally_streaming`` attribute is used to mark a function's body +as being executed with ``PSTATE.SM=1``. The function's interface is +non-streaming, meaning that it is expected to be called with +``PSTATE.SM=0`` and return with ``PSTATE.SM=0``. + +By adding this attribute, Clang will insert the appropriate ``smstart`` and +``smstop`` instructions in the prologue and epilogue of the function to ensure +the body is executed with ``PSTATE.SM=1``. + }]; +} + +def ArmSmeStreamingCompatibleDocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +The ``arm_streaming_compatible`` attribute is used to mark a function as +being a streaming compatible function for which ``PSTATE.SM`` can either be +``0`` or ``1`` at runtime. + +Clang will try to avoid generating instructions that are not legal in +streaming-compatible mode. These are instructions that are valid +exclusively when ``PSTATE.SM=0`` or instructions that are valid exclusively +when ``PSTATE.SM=1``. + }]; +} + +def ArmSmeSharedZADocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +The ``arm_shared_za`` attribute is used to mark a function as sharing the +state of ZA, the accumulator array, with that of its callers. + +By adding this attribute, callers of this function will know that the contents +of ZA may be used for passing or returning data, and can be modified. Clang may +assume that ``PSTATE.ZA`` is ``1`` and will avoid setting up a lazy-save +mechanism for calls to functions marked as ``arm_shared_za``. + }]; +} + +def ArmSmeNewZADocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +The ``arm_new_za`` attribute is used to mark a function as a private ZA +function that requires a new state for ZA. + +By adding this attribute, Clang emits the appropriate ``smstart`` instruction to +allow the use of ZA and will additionally commit a lazy-save if the state of ZA +is dormant. It also emits the appropriate ``smstop`` in the function's epilogue. + }]; +} + +def ArmSmePreservesZADocs : Documentation { + let Category = DocCatACLE_SME; + let Content = [{ +When a function is marked as ``arm_preserves_za``, it is a hint to +the compiler that the function and any of it's callees will preserve the state +of ZA. + }]; +} + + def ArmMveStrictPolymorphismDocs : Documentation { let Category = DocCatType; let Content = [{ 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 @@ -2003,6 +2003,10 @@ "than the function it overrides}1,2">; def note_overridden_virtual_function : Note< "overridden virtual function is here">; +def err_conflicting_overriding_attributes : Error< + "virtual function %0 has different attributes " + "%diff{($) than the function it overrides (which has $)|" + "than the function it overrides}1,2">; def err_conflicting_overriding_cc_attributes : Error< "virtual function %0 has different calling convention attributes " "%diff{($) than the function it overrides (which has calling convention $)|" @@ -3546,6 +3550,9 @@ "the vecreturn attribute can only be used on a class or structure with one member, which must be a vector">; def err_attribute_vecreturn_only_pod_record : Error< "the vecreturn attribute can only be used on a POD (plain old data) class or structure (i.e. no virtual functions)">; +def err_sme_attr_mismatch : Error< + "function declared '%0' was previously declared '%1'" + " with different SME function attributes">; def err_cconv_change : Error< "function declared '%0' here was previously declared " "%select{'%2'|without calling convention}1">; 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 @@ -6942,6 +6942,8 @@ NestedNameSpecInfo &IdInfo, bool EnteringContext); + bool IsInvalidSMECallConversion(QualType FromType, QualType ToType); + /// The parser has parsed a nested-name-specifier /// 'template[opt] template-name < template-args >::'. /// 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 @@ -3198,6 +3198,12 @@ argSlot[i] = params[i]; } + // Propagate the SME ACLE attributes. + if (epi.AArch64SMEAttributes != SME_NormalFunction) { + auto &ExtraBits = *getTrailingObjects(); + ExtraBits.AArch64SMEAttributes = epi.AArch64SMEAttributes; + } + // Fill in the exception type array if present. if (getExceptionSpecType() == EST_Dynamic) { auto &ExtraBits = *getTrailingObjects(); @@ -3391,6 +3397,8 @@ for (unsigned i = 0; i != NumParams; ++i) ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue()); } + ID.AddInteger(epi.AArch64SMEAttributes); + epi.ExtInfo.Profile(ID); ID.AddBoolean(epi.HasTrailingReturn); } diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp --- a/clang/lib/AST/TypePrinter.cpp +++ b/clang/lib/AST/TypePrinter.cpp @@ -909,6 +909,24 @@ FunctionType::ExtInfo Info = T->getExtInfo(); + if ((T->getAArch64SMEAttributes() & + FunctionType::SME_PStateSMCompatibleMask) && + !InsideCCAttribute) + OS << " __attribute__((arm_streaming_compatible))"; + if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) && + !InsideCCAttribute) + OS << " __attribute__((arm_streaming))"; + if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZANewMask) && + !InsideCCAttribute) + OS << " __attribute__((arm_new_za))"; + if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask) && + !InsideCCAttribute) + OS << " __attribute__((arm_shared_za))"; + if ((T->getAArch64SMEAttributes() & + FunctionType::SME_PStateZAPreservedMask) && + !InsideCCAttribute) + OS << " __attribute__((arm_preserves_za))"; + printFunctionAfter(Info, OS); if (!T->getMethodQuals().empty()) @@ -1786,6 +1804,11 @@ break; } case attr::AArch64VectorPcs: OS << "aarch64_vector_pcs"; break; + case attr::ArmStreaming: OS << "arm_streaming"; break; + case attr::ArmStreamingCompatible: OS << "arm_streaming_compatible"; break; + case attr::ArmNewZA: OS << "arm_new_za"; break; + case attr::ArmPreservesZA: OS << "arm_preserves_za"; break; + case attr::ArmSharedZA: OS << "arm_shared_za"; break; case attr::AArch64SVEPcs: OS << "aarch64_sve_pcs"; break; case attr::AMDGPUKernelCall: OS << "amdgpu_kernel"; break; case attr::IntelOclBicc: OS << "inteloclbicc"; break; diff --git a/clang/lib/Basic/Targets/AArch64.h b/clang/lib/Basic/Targets/AArch64.h --- a/clang/lib/Basic/Targets/AArch64.h +++ b/clang/lib/Basic/Targets/AArch64.h @@ -55,6 +55,7 @@ bool HasFlagM; bool HasMOPS; bool HasRCPC; + bool HasSME; llvm::AArch64::ArchKind ArchKind; diff --git a/clang/lib/Basic/Targets/AArch64.cpp b/clang/lib/Basic/Targets/AArch64.cpp --- a/clang/lib/Basic/Targets/AArch64.cpp +++ b/clang/lib/Basic/Targets/AArch64.cpp @@ -547,6 +547,7 @@ .Cases("aarch64", "arm64", "arm", true) .Case("neon", FPU & NeonMode) .Cases("sve", "sve2", "sve2-bitperm", "sve2-aes", "sve2-sha3", "sve2-sm4", "f64mm", "f32mm", "i8mm", "bf16", FPU & SveMode) + .Case("sme", HasSME) .Case("ls64", HasLS64) .Default(false); } @@ -595,12 +596,18 @@ HasLSE = false; HasMOPS = false; HasRCPC = false; + HasSME = false; ArchKind = llvm::AArch64::ArchKind::INVALID; for (const auto &Feature : Features) { if (Feature == "+neon") FPU |= NeonMode; + if (Feature == "+sme") { + HasSME = true; + HasBFloat16 = true; + HasFullFP16 = true; + } if (Feature == "+sve") { FPU |= SveMode; HasFullFP16 = true; diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp --- a/clang/lib/CodeGen/CGCall.cpp +++ b/clang/lib/CodeGen/CGCall.cpp @@ -1765,6 +1765,17 @@ if (!isUnresolvedExceptionSpec(FPT->getExceptionSpecType()) && FPT->isNothrow()) FuncAttrs.addAttribute(llvm::Attribute::NoUnwind); + + if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask) + FuncAttrs.addAttribute("aarch64_pstate_sm_enabled"); + if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask) + FuncAttrs.addAttribute("aarch64_pstate_sm_compatible"); + if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZANewMask) + FuncAttrs.addAttribute("aarch64_pstate_za_new"); + if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask) + FuncAttrs.addAttribute("aarch64_pstate_za_shared"); + if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZAPreservedMask) + FuncAttrs.addAttribute("aarch64_pstate_za_preserved"); } static void AddAttributesFromAssumes(llvm::AttrBuilder &FuncAttrs, @@ -2226,6 +2237,9 @@ llvm::toStringRef(CodeGenOpts.UniformWGSize)); } } + + if (TargetDecl->hasAttr()) + FuncAttrs.addAttribute("aarch64_pstate_sm_body"); } // Attach "no-builtins" attributes to: diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1989,6 +1989,12 @@ return; } + // We only need to handle the 'arm_locally_streaming' attribute as a + // special case here (as opposed to e.g. 'arm_streaming'), because it + // is not set from the prototype, but rather from the definition. + if (D->hasAttr()) + B.addAttribute("aarch64_pstate_sm_body"); + // Track whether we need to add the optnone LLVM attribute, // starting with the default for this optimization level. bool ShouldAddOptNone = 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 @@ -3743,6 +3743,15 @@ } } + // It is not allowed to redeclare an SME function with different SME + // attributes. + if (IsInvalidSMECallConversion(Old->getType(), New->getType())) { + Diag(New->getLocation(), diag::err_sme_attr_mismatch) + << New->getType() << Old->getType(); + Diag(OldLocation, diag::note_previous_declaration); + return true; + } + // If a function is first declared with a calling convention, but is later // declared or defined without one, all following decls assume the calling // convention of the first. diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -9203,6 +9203,10 @@ handleArmBuiltinAliasAttr(S, D, AL); break; + case ParsedAttr::AT_ArmLocallyStreaming: + handleSimpleAttribute(S, D, AL); + break; + case ParsedAttr::AT_AcquireHandle: handleAcquireHandleAttr(S, D, AL); break; 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 @@ -17687,6 +17687,14 @@ } } + // SME attributes must match when overriding a function declaration. + if (IsInvalidSMECallConversion(Old->getType(), New->getType())) { + Diag(New->getLocation(), diag::err_conflicting_overriding_attributes) + << New << New->getType() << Old->getType(); + Diag(Old->getLocation(), diag::note_overridden_virtual_function); + return true; + } + // Virtual overrides must have the same code_seg. const auto *OldCSA = Old->getAttr(); const auto *NewCSA = New->getAttr(); 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 @@ -9216,6 +9216,30 @@ ColonLoc, result, VK, OK); } +// Check that the SME attributes for PSTATE.ZA and PSTATE.SM are compatible. +bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType) { + unsigned FromAttributes = 0, ToAttributes = 0; + if (const auto *FromFn = + dyn_cast(Context.getCanonicalType(FromType))) + FromAttributes = + FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + if (const auto *ToFn = + dyn_cast(Context.getCanonicalType(ToType))) + ToAttributes = + ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask; + + if (FromAttributes == ToAttributes) + return false; + + // Make an exception for preserves_za, which can be dropped because it's + // only a hint. + unsigned Changed = FromAttributes ^ ToAttributes; + bool DropsPreservesZA = + Changed == FunctionType::SME_PStateZAPreservedMask && + (ToAttributes & FunctionType::SME_PStateZAPreservedMask); + return !DropsPreservesZA; +} + // Check if we have a conversion between incompatible cmse function pointer // types, that is, a conversion between a function pointer with the // cmse_nonsecure_call attribute and one without. @@ -9382,6 +9406,8 @@ return Sema::IncompatibleFunctionPointer; if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans)) return Sema::IncompatibleFunctionPointer; + if (S.IsInvalidSMECallConversion(ltrans, rtrans)) + return Sema::IncompatibleFunctionPointer; return ConvTy; } 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 @@ -1685,6 +1685,26 @@ Changed = true; } + // Drop the 'arm_preserves_za' if not present in the target type (we can do + // that because it is merely a hint). + if (const auto *FromFPT = dyn_cast(FromFn)) { + FunctionProtoType::ExtProtoInfo ExtInfo = FromFPT->getExtProtoInfo(); + if (ExtInfo.AArch64SMEAttributes & + FunctionType::SME_PStateZAPreservedMask) { + unsigned ToFlags = 0; + if (const auto *ToFPT = dyn_cast(ToFn)) + ToFlags = ToFPT->getExtProtoInfo().AArch64SMEAttributes; + if (!(ToFlags & FunctionType::SME_PStateZAPreservedMask)) { + ExtInfo.setArmSMEAttribute(FunctionType::SME_PStateZAPreservedMask, + false); + QualType QT = Context.getFunctionType( + FromFPT->getReturnType(), FromFPT->getParamTypes(), ExtInfo); + FromFn = QT->getAs(); + Changed = true; + } + } + } + // Drop 'noexcept' if not present in target type. if (const auto *FromFPT = dyn_cast(FromFn)) { const auto *ToFPT = cast(ToFn); 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 @@ -138,6 +138,11 @@ case ParsedAttr::AT_NoReturn: \ case ParsedAttr::AT_Regparm: \ case ParsedAttr::AT_CmseNSCall: \ + case ParsedAttr::AT_ArmStreaming: \ + case ParsedAttr::AT_ArmStreamingCompatible: \ + case ParsedAttr::AT_ArmNewZA: \ + case ParsedAttr::AT_ArmSharedZA: \ + case ParsedAttr::AT_ArmPreservesZA: \ case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \ case ParsedAttr::AT_AnyX86NoCfCheck: \ CALLING_CONV_ATTRS_CASELIST @@ -7642,6 +7647,24 @@ llvm_unreachable("unexpected attribute kind!"); } +static bool checkMutualExclusion(TypeProcessingState &state, + const FunctionProtoType::ExtProtoInfo &EPI, + ParsedAttr &Attr, + AttributeCommonInfo::Kind OtherKind) { + auto OtherAttr = std::find_if( + state.getCurrentAttributes().begin(), state.getCurrentAttributes().end(), + [OtherKind](const ParsedAttr &A) { return A.getKind() == OtherKind; }); + if (OtherAttr == state.getCurrentAttributes().end() || OtherAttr->isInvalid()) + return false; + + Sema &S = state.getSema(); + S.Diag(Attr.getLoc(), diag::err_attributes_are_not_compatible) + << *OtherAttr << Attr; + S.Diag(OtherAttr->getLoc(), diag::note_conflicting_attribute); + Attr.setInvalid(); + return true; +} + /// Process an individual function attribute. Returns true to /// indicate that the attribute was handled, false if it wasn't. static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr, @@ -7771,6 +7794,65 @@ return true; } + if (attr.getKind() == ParsedAttr::AT_ArmStreaming || + attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible || + attr.getKind() == ParsedAttr::AT_ArmNewZA || + attr.getKind() == ParsedAttr::AT_ArmSharedZA || + attr.getKind() == ParsedAttr::AT_ArmPreservesZA){ + if (S.CheckAttrTarget(attr) || S.CheckAttrNoArgs(attr)) + return true; + + if (!unwrapped.isFunctionType()) + return false; + + const FunctionProtoType *FnTy = unwrapped.get()->getAs(); + if (!FnTy) { + // SME ACLE attributes are not supported on K&R-style unprototyped C + // functions. + S.Diag(attr.getLoc(), diag::warn_attribute_ignored) << attr; + attr.setInvalid(); + return false; + } + + FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo(); + switch (attr.getKind()) { + case ParsedAttr::AT_ArmStreaming: + if (checkMutualExclusion(state, EPI, attr, + ParsedAttr::AT_ArmStreamingCompatible)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateSMEnabledMask); + break; + case ParsedAttr::AT_ArmStreamingCompatible: + if (checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmStreaming)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateSMCompatibleMask); + break; + case ParsedAttr::AT_ArmNewZA: + if (checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmSharedZA) || + checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmPreservesZA)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateZANewMask); + break; + case ParsedAttr::AT_ArmSharedZA: + if (checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmNewZA)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateZASharedMask); + break; + case ParsedAttr::AT_ArmPreservesZA: + if (checkMutualExclusion(state, EPI, attr, ParsedAttr::AT_ArmNewZA)) + return true; + EPI.setArmSMEAttribute(FunctionType::SME_PStateZAPreservedMask); + break; + default: + llvm_unreachable("Unsupported attribute"); + } + + QualType newtype = S.Context.getFunctionType(FnTy->getReturnType(), + FnTy->getParamTypes(), EPI); + type = unwrapped.wrap(S, newtype->getAs()); + return true; + } + if (attr.getKind() == ParsedAttr::AT_NoThrow) { // Delay if this is not a function type. if (!unwrapped.isFunctionType()) diff --git a/clang/test/AST/ast-dump-sme-attributes.cpp b/clang/test/AST/ast-dump-sme-attributes.cpp new file mode 100644 --- /dev/null +++ b/clang/test/AST/ast-dump-sme-attributes.cpp @@ -0,0 +1,65 @@ +// Test without serialization: +// RUN: %clang_cc1 -triple aarch64 -target-feature +sme -std=c++2a -ast-dump -ast-dump-filter Foo %s | FileCheck -strict-whitespace %s + +// Test with serialization: +// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme -emit-pch -o %t %s +// RUN: %clang_cc1 -x c++ -std=c++20 -triple aarch64 -target-feature +sme -include-pch %t -ast-dump-all -ast-dump-filter Foo /dev/null \ +// RUN: | sed -e "s/ //" -e "s/ imported//" \ +// RUN: | FileCheck --strict-whitespace %s + +struct Foo { +// CHECK: |-CXXRecordDecl {{.*}} implicit struct Foo +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming 'void () __attribute__((arm_streaming))' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming_compatible 'void () __attribute__((arm_streaming_compatible))' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_locally_streaming 'void ()' +// CHECK-NEXT: | `-ArmLocallyStreamingAttr {{.*}} +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_shared_za 'void () __attribute__((arm_shared_za))' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_new_za 'void () __attribute__((arm_new_za))' +// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_preserves_za 'void () __attribute__((arm_preserves_za))' + void f_streaming() __attribute__((arm_streaming)); + void f_streaming_compatible() __attribute__((arm_streaming_compatible)); + void f_locally_streaming() __attribute__((arm_locally_streaming)); + void f_shared_za() __attribute__((arm_shared_za)); + void f_new_za() __attribute__((arm_new_za)); + void f_preserves_za() __attribute__((arm_preserves_za)); + + +// CHECK: |-CXXMethodDecl {{.*}} test_lambda 'int (int)' implicit-inline +// CHECK: `-CompoundStmt +// CHECK-NEXT: |-DeclStmt +// CHECK-NEXT: | `-VarDecl +// CHECK-NEXT: | `-LambdaExpr +// CHECK-NEXT: | |-CXXRecordDecl +// CHECK: | | |-CXXMethodDecl {{.*}} used constexpr operator() 'int (int) __attribute__((arm_streaming)) const' inline +// CHECK: | | |-CXXConversionDecl {{.*}} implicit constexpr operator int (*)(int) __attribute__((arm_streaming)) 'int (*() const noexcept)(int) __attribute__((arm_streaming))' inline +// CHECK: | | |-CXXMethodDecl {{.*}} implicit __invoke 'int (int) __attribute__((arm_streaming))' static inline +// CHECK: `-ReturnStmt +// CHECK: `-CXXOperatorCallExpr +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int (*)(int) __attribute__((arm_streaming)) const' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int (int) __attribute__((arm_streaming)) const' lvalue CXXMethod {{.*}} 'operator()' 'int (int) __attribute__((arm_streaming)) const' + int test_lambda(int x) { + auto F = [](int x) __attribute__((arm_streaming)) { return x; }; + return F(x); + } + +// CHECK: |-TypedefDecl {{.*}} referenced s_ptrty 'void (*)(int, int) __attribute__((arm_streaming))' +// CHECK-NEXT: | `-PointerType {{.*}} 'void (*)(int, int) __attribute__((arm_streaming))' +// CHECK-NEXT: | `-ParenType {{.*}} 'void (int, int) __attribute__((arm_streaming))' sugar +// CHECK-NEXT: | `-FunctionProtoType {{.*}} 'void (int, int) __attribute__((arm_streaming))' cdecl + typedef void __attribute__((arm_streaming)) (*s_ptrty) (int, int); + +// CHECK: `-CXXMethodDecl {{.*}} test_streaming_ptrty 'void (s_ptrty, int, int)' implicit-inline +// CHECK-NEXT: |-ParmVarDecl {{.*}} used f 's_ptrty':'void (*)(int, int) __attribute__((arm_streaming))' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used x 'int' +// CHECK-NEXT: |-ParmVarDecl {{.*}} used y 'int' +// CHECK: `-CompoundStmt +// CHECK-NEXT: `-ReturnStmt +// CHECK-NEXT: `-CallExpr +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 's_ptrty':'void (*)(int, int) __attribute__((arm_streaming))' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 's_ptrty':'void (*)(int, int) __attribute__((arm_streaming))' lvalue ParmVar {{.*}} 'f' 's_ptrty':'void (*)(int, int) __attribute__((arm_streaming))' +// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int' +// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' +// CHECK-NEXT: `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'y' 'int' + void test_streaming_ptrty(s_ptrty f, int x, int y) { return f(x, y); }; +}; diff --git a/clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp b/clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp @@ -0,0 +1,280 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \ +// RUN: -S -O0 -Werror -emit-llvm -o - %s | FileCheck %s + +extern "C" { + +extern int normal_callee(); + +// == FUNCTION DECLARATIONS == + +__attribute__((arm_streaming)) int streaming_decl(); +__attribute__((arm_streaming_compatible)) int streaming_compatible_decl(); +__attribute__((arm_shared_za)) int shared_za_decl(); +__attribute__((arm_preserves_za)) int preserves_za_decl(); +__attribute__((arm_new_za)) int new_za_decl(); + +// == FUNCTION DEFINITIONS == + +// CHECK-LABEL: @streaming_caller() +// CHECK-SAME: #[[SM_ENABLED:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_streaming)) int streaming_caller() { + return normal_callee(); +} + +// CHECK: declare i32 @normal_callee() #[[NORMAL_DECL:[0-9]+]] + + +// CHECK-LABEL: @streaming_callee() +// CHECK-SAME: #[[SM_ENABLED]] +// CHECK: call i32 @streaming_decl() #[[SM_ENABLED_CALL:[0-9]+]] +// +__attribute__((arm_streaming)) int streaming_callee() { + return streaming_decl(); +} + +// CHECK: declare i32 @streaming_decl() #[[SM_ENABLED_DECL:[0-9]+]] + +// CHECK-LABEL: @streaming_compatible_caller() +// CHECK-SAME: #[[SM_COMPATIBLE:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_streaming_compatible)) int streaming_compatible_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @streaming_compatible_callee() +// CHECK-SAME: #[[SM_COMPATIBLE]] +// CHECK: call i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_CALL:[0-9]+]] +// +__attribute__((arm_streaming_compatible)) int streaming_compatible_callee() { + return streaming_compatible_decl(); +} + +// CHECK: declare i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_DECL:[0-9]+]] + +// CHECK-LABEL: @locally_streaming_caller() +// CHECK-SAME: #[[SM_BODY:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_locally_streaming)) int locally_streaming_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @locally_streaming_callee() +// CHECK-SAME: #[[SM_BODY]] +// CHECK: call i32 @locally_streaming_caller() #[[SM_BODY_CALL:[0-9]+]] +// +__attribute__((arm_locally_streaming)) int locally_streaming_callee() { + return locally_streaming_caller(); +} + + +// CHECK-LABEL: @shared_za_caller() +// CHECK-SAME: #[[ZA_SHARED:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_shared_za)) int shared_za_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @shared_za_callee() +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call i32 @shared_za_decl() #[[ZA_SHARED_CALL:[0-9]+]] +// +__attribute__((arm_shared_za)) int shared_za_callee() { + return shared_za_decl(); +} + +// CHECK: declare i32 @shared_za_decl() #[[ZA_SHARED_DECL:[0-9]+]] + + +// CHECK-LABEL: @preserves_za_caller() +// CHECK-SAME: #[[ZA_PRESERVED:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_preserves_za)) int preserves_za_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @preserves_za_callee() +// CHECK-SAME: #[[ZA_PRESERVED]] +// CHECK: call i32 @preserves_za_decl() #[[ZA_PRESERVED_CALL:[0-9]+]] +// +__attribute__((arm_preserves_za)) int preserves_za_callee() { + return preserves_za_decl(); +} + +// CHECK: declare i32 @preserves_za_decl() #[[ZA_PRESERVED_DECL:[0-9]+]] + + +// CHECK-LABEL: @new_za_caller() +// CHECK-SAME: #[[ZA_NEW:[0-9]+]] +// CHECK: call i32 @normal_callee() +// +__attribute__((arm_new_za)) int new_za_caller() { + return normal_callee(); +} + +// CHECK-LABEL: @new_za_callee() +// CHECK-SAME: #[[ZA_NEW]] +// CHECK: call i32 @new_za_decl() #[[ZA_NEW_CALL:[0-9]+]] +// +__attribute__((arm_new_za)) int new_za_callee() { + return new_za_decl(); +} + +// CHECK: declare i32 @new_za_decl() #[[ZA_NEW_DECL:[0-9]+]] + + +// Ensure that the attributes are correctly propagated to function types +// and also to callsites. +typedef void __attribute__((arm_streaming)) (*s_ptrty) (int, int); +typedef void __attribute__((arm_streaming_compatible)) (*sc_ptrty) (int, int); +typedef void __attribute__((arm_new_za)) (*nz_ptrty) (int, int); +typedef void __attribute__((arm_shared_za)) (*sz_ptrty) (int, int); +typedef void __attribute__((arm_preserves_za)) (*pz_ptrty) (int, int); + +// CHECK-LABEL: @test_streaming_ptrty( +// CHECK-SAME: #[[NORMAL_DEF:[0-9]+]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_ENABLED_CALL]] +// +void test_streaming_ptrty(s_ptrty f, int x, int y) { return f(x, y); } +// CHECK-LABEL: @test_streaming_compatible_ptrty( +// CHECK-SAME: #[[NORMAL_DEF]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_COMPATIBLE_CALL]] +// +void test_streaming_compatible_ptrty(sc_ptrty f, int x, int y) { return f(x, y); } +// CHECK-LABEL: @test_new_za( +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_NEW_CALL]] +// +void __attribute__((arm_shared_za)) test_new_za(nz_ptrty f, int x, int y) { return f(x, y); } +// CHECK-LABEL: @test_shared_za( +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_SHARED_CALL]] +// +void __attribute__((arm_shared_za)) test_shared_za(sz_ptrty f, int x, int y) { return f(x, y); } +// CHECK-LABEL: @test_preserved_za( +// CHECK-SAME: #[[ZA_SHARED]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_PRESERVED_CALL]] +// +void __attribute__((arm_shared_za)) test_preserved_za(pz_ptrty f, int x, int y) { return f(x, y); } + +// CHECK-LABEL: @test_indirect_streaming_ptrty( +// CHECK-SAME: #[[NORMAL_DEF:[0-9]+]] +// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[SM_ENABLED_CALL]] +// +typedef s_ptrty **indirect_s_ptrty; +void test_indirect_streaming_ptrty(indirect_s_ptrty fptr, int x, int y) { return (**fptr)(x, y); } +} // extern "C" + +// +// Test that having the attribute in different places (on declaration and on type) +// both results in the attribute being applied to the type. +// + +// CHECK-LABEL: @_Z24test_same_type_streamingv( +// CHECK: call void @_Z10streaming1v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z10streaming2v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z10streaming3v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z20same_type_streaming1v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z20same_type_streaming2v() #[[SM_ENABLED_CALL]] +// CHECK: call void @_Z20same_type_streaming3v() #[[SM_ENABLED_CALL]] +// CHECK: ret void +// CHECK: } +// CHECK: declare void @_Z10streaming1v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z10streaming2v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z10streaming3v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z20same_type_streaming1v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z20same_type_streaming2v() #[[SM_ENABLED_DECL]] +// CHECK: declare void @_Z20same_type_streaming3v() #[[SM_ENABLED_DECL]] +__attribute__((arm_streaming)) void streaming1(); +void __attribute__((arm_streaming)) streaming2(); +void streaming3() __attribute__((arm_streaming)); +decltype(streaming1) same_type_streaming1; +decltype(streaming2) same_type_streaming2; +decltype(streaming3) same_type_streaming3; +void test_same_type_streaming() { + streaming1(); + streaming2(); + streaming3(); + same_type_streaming1(); + same_type_streaming2(); + same_type_streaming3(); +} + +// +// Test overloading; the attribute is not required for overloaded types and +// does not apply if not specified. +// + +// CHECK-LABEL: @_Z12overloadedfni( +// CHECK-SAME: #[[SM_ENABLED]] +int __attribute__((arm_streaming)) overloadedfn(int x) { return x; } +// CHECK-LABEL: @_Z12overloadedfnf( +// CHECK-SAME: #[[NORMAL_DEF]] +// +float overloadedfn(float x) { return x; } +// CHECK-LABEL: @_Z13test_overloadi( +// CHECK-SAME: #[[NORMAL_DEF]] +// +int test_overload(int x) { return overloadedfn(x); } +// CHECK-LABEL: @_Z13test_overloadf( +// CHECK-SAME: #[[NORMAL_DEF]] +// +float test_overload(float x) { return overloadedfn(x); } + +// CHECK-LABEL: @_Z11test_lambdai( +// CHECK-SAME: #[[NORMAL_DEF]] +// CHECK: call noundef i32 @"_ZZ11test_lambdaiENK3$_0clEi"({{.*}}) #[[SM_ENABLED_CALL]] +// +// CHECK: @"_ZZ11test_lambdaiENK3$_0clEi"( +// CHECK-SAME: #[[SM_ENABLED]] +int test_lambda(int x) { + auto F = [](int x) __attribute__((arm_streaming)) { return x; }; + return F(x); +} + +// CHECK-LABEL: @_Z27test_template_instantiationv( +// CHECK-SAME: #[[NORMAL_DEF]] +// CHECK: call noundef i32 @_Z15template_functyIiET_S0_(i32 noundef 12) #[[SM_ENABLED_CALL]] +// +// CHECK: @_Z15template_functyIiET_S0_( +// CHECK-SAME: #[[SM_ENABLED]] +template +Ty template_functy(Ty x) __attribute__((arm_streaming)) { return x; } +int test_template_instantiation() { return template_functy(12); } + +// +// Test that arm_locally_streaming is inherited by future redeclarations, +// even when they don't specify the attribute. +// + +// CHECK: define {{.*}} @_Z25locally_streaming_inheritv( +// CHECK-SAME: #[[SM_BODY]] +__attribute__((arm_locally_streaming)) void locally_streaming_inherit(); +void locally_streaming_inherit() { + streaming_decl(); +} + +// CHECK: attributes #[[SM_ENABLED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_enabled" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[NORMAL_DECL]] = { "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[SM_ENABLED_DECL]] = { "aarch64_pstate_sm_enabled" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[SM_COMPATIBLE]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_compatible" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[SM_COMPATIBLE_DECL]] = { "aarch64_pstate_sm_compatible" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[SM_BODY]] = { mustprogress noinline nounwind optnone "aarch64_pstate_sm_body" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_SHARED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_shared" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_SHARED_DECL]] = { "aarch64_pstate_za_shared" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_PRESERVED]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_preserved" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_PRESERVED_DECL]] = { "aarch64_pstate_za_preserved" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_NEW]] = { mustprogress noinline nounwind optnone "aarch64_pstate_za_new" "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[ZA_NEW_DECL]] = { "aarch64_pstate_za_new" "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[NORMAL_DEF]] = { mustprogress noinline nounwind optnone "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+sme" } +// CHECK: attributes #[[SM_ENABLED_CALL]] = { "aarch64_pstate_sm_enabled" } +// CHECK: attributes #[[SM_COMPATIBLE_CALL]] = { "aarch64_pstate_sm_compatible" } +// CHECK: attributes #[[SM_BODY_CALL]] = { "aarch64_pstate_sm_body" } +// CHECK: attributes #[[ZA_SHARED_CALL]] = { "aarch64_pstate_za_shared" } +// CHECK: attributes #[[ZA_PRESERVED_CALL]] = { "aarch64_pstate_za_preserved" } +// CHECK: attributes #[[ZA_NEW_CALL]] = { "aarch64_pstate_za_new" } diff --git a/clang/test/Misc/pragma-attribute-supported-attributes-list.test b/clang/test/Misc/pragma-attribute-supported-attributes-list.test --- a/clang/test/Misc/pragma-attribute-supported-attributes-list.test +++ b/clang/test/Misc/pragma-attribute-supported-attributes-list.test @@ -18,6 +18,7 @@ // CHECK-NEXT: AnyX86NoCfCheck (SubjectMatchRule_hasType_functionType) // CHECK-NEXT: ArcWeakrefUnavailable (SubjectMatchRule_objc_interface) // CHECK-NEXT: ArmBuiltinAlias (SubjectMatchRule_function) +// CHECK-NEXT: ArmLocallyStreaming (SubjectMatchRule_function) // CHECK-NEXT: AssumeAligned (SubjectMatchRule_objc_method, SubjectMatchRule_function) // CHECK-NEXT: Assumption (SubjectMatchRule_function, SubjectMatchRule_objc_method) // CHECK-NEXT: Availability ((SubjectMatchRule_record, SubjectMatchRule_enum, SubjectMatchRule_enum_constant, SubjectMatchRule_field, SubjectMatchRule_function, SubjectMatchRule_namespace, SubjectMatchRule_objc_category, SubjectMatchRule_objc_implementation, SubjectMatchRule_objc_interface, SubjectMatchRule_objc_method, SubjectMatchRule_objc_property, SubjectMatchRule_objc_protocol, SubjectMatchRule_record, SubjectMatchRule_type_alias, SubjectMatchRule_variable)) diff --git a/clang/test/Sema/aarch64-sme-attrs-no-sme.c b/clang/test/Sema/aarch64-sme-attrs-no-sme.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/aarch64-sme-attrs-no-sme.c @@ -0,0 +1,36 @@ +// Test that the attribute is ignored if we don't compile for both AArch64 with +sme. +// +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-none-linux-gnu -target-feature +sme -fsyntax-only -verify %s + +extern int normal_callee(void); + +// expected-warning@+1 {{unknown attribute 'arm_streaming' ignored}} +__attribute__((arm_streaming)) +int streaming_caller(void) { + return normal_callee(); +} + +// expected-warning@+1 {{unknown attribute 'arm_locally_streaming' ignored}} +__attribute__((arm_locally_streaming)) +int locally_streaming_caller(void) { + return normal_callee(); +} + +// expected-warning@+1 {{unknown attribute 'arm_shared_za' ignored}} +__attribute__((arm_shared_za)) +int shared_za_caller(void) { + return normal_callee(); +} + +// expected-warning@+1 {{unknown attribute 'arm_preserves_za' ignored}} +__attribute__((arm_preserves_za)) +int preserves_za_caller(void) { + return normal_callee(); +} + +// expected-warning@+1 {{unknown attribute 'arm_new_za' ignored}} +__attribute__((arm_new_za)) +int new_za_caller(void) { + return normal_callee(); +} diff --git a/clang/test/Sema/aarch64-sme-func-attrs.c b/clang/test/Sema/aarch64-sme-func-attrs.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/aarch64-sme-func-attrs.c @@ -0,0 +1,278 @@ +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme -fsyntax-only -verify=expected-cpp -x c++ %s + +// Valid attributes + +__attribute__((arm_streaming)) void sme_arm_streaming(void); +__attribute__((arm_streaming_compatible)) void sme_arm_streaming_compatible(void); + +__attribute__((arm_new_za)) void sme_arm_new_za(void); +__attribute__((arm_shared_za)) void sme_arm_shared_za(void); +__attribute__((arm_preserves_za)) void sme_arm_preserves_za(void); + +__attribute__((arm_streaming, arm_new_za)) void sme_arm_streaming_new_za(void); +__attribute__((arm_streaming, arm_shared_za)) void sme_arm_streaming_shared_za(void); +__attribute__((arm_streaming, arm_preserves_za)) void sme_arm_streaming_preserves_za(void); + +__attribute__((arm_streaming_compatible, arm_new_za)) void sme_arm_sc_new_za(void); +__attribute__((arm_streaming_compatible, arm_shared_za)) void sme_arm_sc_shared_za(void); +__attribute__((arm_streaming_compatible, arm_preserves_za)) void sme_arm_sc_preserves_za(void); + +__attribute__((arm_shared_za, arm_preserves_za)) void sme_arm_shared_preserves_za(void); + +__attribute__((arm_locally_streaming)) void sme_arm_locally_streaming(void) { } +__attribute__((arm_locally_streaming, arm_streaming)) void sme_arm_streaming_and_locally_streaming(void) { } +__attribute__((arm_locally_streaming, arm_streaming_compatible)) void sme_arm_streaming_and_streaming_compatible(void) { } + +__attribute__((arm_locally_streaming, arm_new_za)) void sme_arm_ls_new_za(void) { } +__attribute__((arm_locally_streaming, arm_shared_za)) void sme_arm_ls_shared_za(void) { } +__attribute__((arm_locally_streaming, arm_preserves_za)) void sme_arm_ls_preserves_za(void) { } + +// Valid attributes on function pointers + +void __attribute__((arm_streaming)) streaming_ptr(void); +typedef __attribute__((arm_streaming)) void (*fptrty1) (void); +fptrty1 call_streaming_func() { return streaming_ptr; } + +void __attribute__((arm_streaming_compatible)) streaming_compatible_ptr(void); +typedef __attribute__((arm_streaming_compatible)) void (*fptrty2) (void); +fptrty2 call_sc_func() { return streaming_compatible_ptr; } + +void __attribute__((arm_new_za)) new_za_ptr(void); +typedef __attribute__((arm_new_za)) void (*fptrty3) (void); +fptrty3 call_new_za_func() { return new_za_ptr; } + +void __attribute__((arm_shared_za)) shared_za_ptr(void); +typedef __attribute__((arm_shared_za)) void (*fptrty4) (void); +fptrty4 call_shared_za_func() { return shared_za_ptr; } + +void __attribute__((arm_preserves_za)) preserves_za_ptr(void); +typedef __attribute__((arm_preserves_za)) void (*fptrty5) (void); +fptrty5 call_preserve_za_func() { return preserves_za_ptr; } + +void __attribute__((arm_shared_za, arm_preserves_za)) shared_preserves_za_ptr(void); +typedef __attribute__((arm_shared_za, arm_preserves_za)) void (*fptrty6) (void); +fptrty6 call_shared_preserve_za_func() { return shared_preserves_za_ptr; } + +typedef void (*fptrty7) (void); +fptrty7 cast_ls_func_to_normal() { return sme_arm_locally_streaming; } + +// FIXME: Add invalid function pointer assignments such as assigning: +// 1. A streaming compatible function to a normal function pointer, +// 2. A locally streaming function to a streaming function pointer, +// etc. + +// Invalid attributes + +// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_streaming, arm_streaming_compatible)) void streaming_mode(void); + +// expected-cpp-error@+4 {{'arm_streaming' and 'arm_streaming_compatible' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_streaming' and 'arm_streaming_compatible' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_streaming_compatible, arm_streaming)) void streaming_compatible(void); + +// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_new_za, arm_shared_za)) void new_shared_za(void); + +// expected-cpp-error@+4 {{'arm_new_za' and 'arm_shared_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_new_za' and 'arm_shared_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_shared_za, arm_new_za)) void shared_new_za(void); + +// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_new_za, arm_preserves_za)) void new_preserves_za(void); + +// expected-cpp-error@+4 {{'arm_new_za' and 'arm_preserves_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_new_za' and 'arm_preserves_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +__attribute__((arm_preserves_za, arm_new_za)) void preserves_new_za(void); + +// Invalid attributes on function pointers + +// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void __attribute__((arm_streaming, arm_streaming_compatible)) streaming_ptr_invalid(void); +// expected-cpp-error@+4 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_streaming_compatible' and 'arm_streaming' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +typedef __attribute__((arm_streaming, arm_streaming_compatible)) void (*fptrty8) (void); +fptrty8 invalid_streaming_func() { return streaming_ptr_invalid; } + +// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void __attribute__((arm_new_za, arm_shared_za)) shared_za_ptr_invalid(void); +// expected-cpp-error@+4 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_shared_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +typedef __attribute__((arm_new_za, arm_shared_za)) void (*fptrty9) (void); +fptrty9 invalid_shared_za_func() { return shared_za_ptr_invalid; } + +// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +void __attribute__((arm_new_za, arm_preserves_za)) preserves_za_ptr_invalid(void); +// expected-cpp-error@+4 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-cpp-note@+3 {{conflicting attribute is here}} +// expected-error@+2 {{'arm_preserves_za' and 'arm_new_za' attributes are not compatible}} +// expected-note@+1 {{conflicting attribute is here}} +typedef __attribute__((arm_new_za, arm_preserves_za)) void (*fptrty10) (void); +fptrty10 invalid_preserve_za_func() { return preserves_za_ptr_invalid; } + +// expected-cpp-error@+2 {{'arm_locally_streaming' attribute only applies to functions}} +// expected-error@+1 {{'arm_locally_streaming' attribute only applies to functions}} +typedef __attribute__((arm_locally_streaming)) void (*fptrty11) (void); + +// expected-warning@+2 {{'arm_streaming' attribute ignored}} +// expected-warning@+1 {{'arm_streaming' only applies to function types; type here is 'void ()'}} +__attribute__((arm_streaming)) void function_no_prototype(); + +// +// Check for incorrect conversions of function pointers with the attributes +// + +typedef void (*n_ptrty) (void); +typedef __attribute__((arm_streaming)) void (*s_ptrty) (void); +s_ptrty return_valid_streaming_fptr(s_ptrty f) { return f; } + +// expected-cpp-error@+2 {{cannot initialize return object of type 's_ptrty' (aka 'void (*)() __attribute__((arm_streaming))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 's_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming))')}} +s_ptrty return_invalid_fptr_streaming_normal(n_ptrty f) { return f; } +// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __attribute__((arm_streaming))')}} +// expected-error@+1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}} +n_ptrty return_invalid_fptr_normal_streaming(s_ptrty f) { return f; } + +// Test an instance where the result type is not a prototyped function, such that we still get a diagnostic. +typedef void (*nonproto_n_ptrty) (); +// expected-cpp-error@+2 {{cannot initialize return object of type 'nonproto_n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __attribute__((arm_streaming))')}} +// expected-error@+1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming))') from a function with result type 'nonproto_n_ptrty' (aka 'void (*)()')}} +nonproto_n_ptrty return_invalid_fptr_streaming_nonprotonormal(s_ptrty f) { return f; } + +typedef __attribute__((arm_streaming_compatible)) void (*sc_ptrty) (void); +sc_ptrty return_valid_streaming_compatible_fptr(sc_ptrty f) { return f; } + +// expected-cpp-error@+2 {{cannot initialize return object of type 'sc_ptrty' (aka 'void (*)() __attribute__((arm_streaming_compatible))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sc_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming_compatible))')}} +sc_ptrty return_invalid_fptr_streaming_compatible_normal(n_ptrty f) { return f; } +// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sc_ptrty' (aka 'void (*)() __attribute__((arm_streaming_compatible))')}} +// expected-error@+1 {{incompatible function pointer types returning 'sc_ptrty' (aka 'void (*)(void) __attribute__((arm_streaming_compatible))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}} +n_ptrty return_invalid_fptr_normal_streaming_compatible(sc_ptrty f) { return f; } + +typedef __attribute__((arm_new_za)) void (*nz_ptrty) (void); +nz_ptrty return_valid_new_za_fptr(nz_ptrty f) { return f; } + +// expected-cpp-error@+2 {{cannot initialize return object of type 'nz_ptrty' (aka 'void (*)() __attribute__((arm_new_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'nz_ptrty' (aka 'void (*)(void) __attribute__((arm_new_za))')}} +nz_ptrty return_invalid_fptr_new_za_normal(n_ptrty f) { return f; } +// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'nz_ptrty' (aka 'void (*)() __attribute__((arm_new_za))')}} +// expected-error@+1 {{incompatible function pointer types returning 'nz_ptrty' (aka 'void (*)(void) __attribute__((arm_new_za))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}} +n_ptrty return_invalid_fptr_normal_new_za(nz_ptrty f) { return f; } + +typedef __attribute__((arm_shared_za)) void (*sz_ptrty) (void); +sz_ptrty return_valid_shared_za_fptr(sz_ptrty f) { return f; } + + +// expected-cpp-error@+2 {{cannot initialize return object of type 'sz_ptrty' (aka 'void (*)() __attribute__((arm_shared_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sz_ptrty' (aka 'void (*)(void) __attribute__((arm_shared_za))')}} +sz_ptrty return_invalid_fptr_shared_za_normal(n_ptrty f) { return f; } +// expected-cpp-error@+2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sz_ptrty' (aka 'void (*)() __attribute__((arm_shared_za))')}} +// expected-error@+1 {{incompatible function pointer types returning 'sz_ptrty' (aka 'void (*)(void) __attribute__((arm_shared_za))') from a function with result type 'n_ptrty' (aka 'void (*)(void)')}} +n_ptrty return_invalid_fptr_normal_shared_za(sz_ptrty f) { return f; } + +typedef __attribute__((arm_preserves_za)) void (*pz_ptrty) (void); +pz_ptrty return_valid_preserves_za_fptr(pz_ptrty f) { return f; } + +// expected-cpp-error@+2 {{cannot initialize return object of type 'pz_ptrty' (aka 'void (*)() __attribute__((arm_preserves_za))') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}} +// expected-error@+1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'pz_ptrty' (aka 'void (*)(void) __attribute__((arm_preserves_za))')}} +pz_ptrty return_invalid_fptr_preserves_za_normal(n_ptrty f) { return f; } +// No diagnostics, the preserves_za hint should be dropped silently. +n_ptrty return_invalid_fptr_normal_preserves_za(pz_ptrty f) { return f; } + +// Test template instantiations +#ifdef __cplusplus +template __attribute__((arm_streaming)) T templated(T x) { return x; } +template <> __attribute__((arm_streaming)) int templated(int x) { return x + 1; } +template <> __attribute__((arm_streaming)) float templated(float x) { return x + 2; } +// expected-cpp-error@+2 {{explicit instantiation of 'templated' does not refer to a function template, variable template, member function, member class, or static data member}} +// expected-cpp-note@-4 {{candidate template ignored: could not match 'short (short) __attribute__((arm_streaming))' against 'short (short)'}} +template short templated(short); +#endif + +// Conflicting attributes on redeclarations + +// expected-error@+5 {{function declared ''void (void) __attribute__((arm_streaming_compatible))'' was previously declared ''void (void) __attribute__((arm_streaming))'' with different SME function attributes}} +// expected-note@+3 {{previous declaration is here}} +// expected-cpp-error@+3 {{function declared ''void () __attribute__((arm_streaming_compatible))'' was previously declared ''void () __attribute__((arm_streaming))'' with different SME function attributes}} +// expected-cpp-note@+1 {{previous declaration is here}} +__attribute__((arm_streaming)) void redecl(void); +__attribute__((arm_streaming_compatible)) void redecl(void) { } + +// expected-error@+5 {{function declared ''void (void) __attribute__((arm_shared_za))'' was previously declared ''void (void) __attribute__((arm_shared_za)) __attribute__((arm_preserves_za))'' with different SME function attributes}} +// expected-note@+3 {{previous declaration is here}} +// expected-cpp-error@+3 {{function declared ''void () __attribute__((arm_shared_za))'' was previously declared ''void () __attribute__((arm_shared_za)) __attribute__((arm_preserves_za))'' with different SME function attributes}} +// expected-cpp-note@+1 {{previous declaration is here}} +__attribute__((arm_shared_za, arm_preserves_za)) void redecl_nopreserve_za(void); +__attribute__((arm_shared_za)) void redecl_nopreserve_za(void) { } + +#ifdef __cplusplus +struct S { + virtual __attribute__((arm_shared_za)) void shared_za_memberfn(void); +}; + +struct S2 : public S { +// expected-cpp-error@+2 {{virtual function 'shared_za_memberfn' has different attributes ('void () __attribute__((arm_new_za))') than the function it overrides (which has 'void () __attribute__((arm_shared_za))')}} +// expected-cpp-note@-5 {{overridden virtual function is here}} + __attribute__((arm_new_za)) void shared_za_memberfn(void) override; +}; + +// Check that the attribute propagates through template instantiations. +template +struct S3 { + static constexpr int value = 0; +}; + +template <> +struct S3 { + static constexpr int value = 1; +}; + +template <> +struct S3 { + static constexpr int value = 2; +}; + +void normal_func() {} +void streaming_func() __attribute__((arm_streaming)) {} + +static_assert(S3::value == 1, "why are we picking the wrong specialization?"); +static_assert(S3::value == 2, "why are we picking the wrong specialization?"); +#endif + +// expected-cpp-error@+2 {{'arm_streaming' attribute takes no arguments}} +// expected-error@+1 {{'arm_streaming' attribute takes no arguments}} +__attribute__((arm_streaming(0))) void invalid_streaming_args(void); + +// expected-cpp-error@+4 {{attribute only applies to non-K&R-style functions}} +// expected-cpp-warning@+3 {{'arm_streaming' only applies to function types; type here is 'int'}} +// expected-error@+2 {{attribute only applies to non-K&R-style functions}} +// expected-warning@+1 {{'arm_streaming' only applies to function types; type here is 'int'}} +__attribute__((arm_streaming)) int invalid_type_for_attribute;