[clang] 28b5f30 - [Clang][AArch64] Add/implement ACLE keywords for SME.
Sander de Smalen via cfe-commits
cfe-commits at lists.llvm.org
Tue Aug 8 00:31:16 PDT 2023
Author: Sander de Smalen
Date: 2023-08-08T07:00:59Z
New Revision: 28b5f3087a3fcd39c80e2b1470a950da17e4cd08
URL: https://github.com/llvm/llvm-project/commit/28b5f3087a3fcd39c80e2b1470a950da17e4cd08
DIFF: https://github.com/llvm/llvm-project/commit/28b5f3087a3fcd39c80e2b1470a950da17e4cd08.diff
LOG: [Clang][AArch64] Add/implement ACLE keywords for SME.
This patch adds all the language-level function keywords defined in:
https://github.com/ARM-software/acle/pull/188 (merged)
https://github.com/ARM-software/acle/pull/261 (update after D148700 landed)
The keywords are used to control PSTATE.ZA and PSTATE.SM, which are
respectively used for enabling the use of the ZA matrix array and Streaming
mode. This information needs to be available on call sites, since the use
of ZA or streaming mode may have to be enabled or disabled around the
call-site (depending on the IR attributes set on the caller and the
callee). For calls to functions from a function pointer, there is no IR
declaration available, so the IR attributes must be added explicitly to the
call-site.
With the exception of '__arm_locally_streaming' and '__arm_new_za' the
information is part of the function's interface, not just the function
definition, and thus needs to be propagated through the
FunctionProtoType::ExtProtoInfo.
This patch adds the defintions of these keywords, as well as codegen and
semantic analysis to ensure conversions between function pointers are valid
and that no conflicting keywords are set. For example, '__arm_streaming'
and '__arm_streaming_compatible' are mutually exclusive.
Differential Revision: https://reviews.llvm.org/D127762
Added:
clang/test/AST/ast-dump-sme-attributes.cpp
clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp
clang/test/Modules/aarch64-sme-keywords.cppm
clang/test/Sema/aarch64-sme-func-attrs.c
Modified:
clang/include/clang/AST/Type.h
clang/include/clang/AST/TypeProperties.td
clang/include/clang/Basic/Attr.td
clang/include/clang/Basic/AttrDocs.td
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/include/clang/Sema/Sema.h
clang/lib/AST/Type.cpp
clang/lib/AST/TypePrinter.cpp
clang/lib/CodeGen/CGCall.cpp
clang/lib/CodeGen/CodeGenModule.cpp
clang/lib/Sema/SemaDecl.cpp
clang/lib/Sema/SemaDeclAttr.cpp
clang/lib/Sema/SemaDeclCXX.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaOverload.cpp
clang/lib/Sema/SemaType.cpp
Removed:
################################################################################
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 7526ec8906a32c..79badb47d181e5 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -3968,6 +3968,19 @@ class FunctionType : public Type {
/// 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_PStateZASharedMask = 1 << 2,
+ SME_PStateZAPreservedMask = 1 << 3,
+ SME_AttributeMask = 0b111'111 // We only support maximum 6 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.
@@ -3975,7 +3988,13 @@ class FunctionType : public Type {
/// The number of types in the exception specification.
/// A whole unsigned is not needed here and according to
/// [implimits] 8 bits would be enough here.
- uint16_t NumExceptionType = 0;
+ unsigned NumExceptionType : 10;
+
+ /// Any AArch64 SME ACLE type attributes that need to be propagated
+ /// on declarations and function pointers.
+ unsigned AArch64SMEAttributes : 6;
+ FunctionTypeExtraBitfields()
+ : NumExceptionType(0), AArch64SMEAttributes(SME_NormalFunction) {}
};
protected:
@@ -4154,18 +4173,22 @@ class FunctionProtoType final
/// the various bits of extra information about a function prototype.
struct ExtProtoInfo {
FunctionType::ExtInfo ExtInfo;
- bool Variadic : 1;
- bool HasTrailingReturn : 1;
+ unsigned Variadic : 1;
+ unsigned HasTrailingReturn : 1;
+ unsigned AArch64SMEAttributes : 6;
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);
@@ -4174,7 +4197,15 @@ class FunctionProtoType final
}
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 &= ~Kind;
}
};
@@ -4301,6 +4332,7 @@ class FunctionProtoType final
EPI.TypeQuals = getMethodQuals();
EPI.RefQualifier = getRefQualifier();
EPI.ExtParameterInfos = getExtParameterInfosOrNull();
+ EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
return EPI;
}
@@ -4482,6 +4514,15 @@ class FunctionProtoType final
return getTrailingObjects<ExtParameterInfo>();
}
+ /// 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<FunctionTypeExtraBitfields>()
+ ->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
index 3cc826c1463a9f..682c869b0c5847 100644
--- a/clang/include/clang/AST/TypeProperties.td
+++ b/clang/include/clang/AST/TypeProperties.td
@@ -323,6 +323,9 @@ let Class = FunctionProtoType in {
? node->getExtParameterInfos()
: llvm::ArrayRef<FunctionProtoType::ExtParameterInfo>() }];
}
+ def : Property<"AArch64SMEAttributes", UInt32> {
+ let Read = [{ node->getAArch64SMEAttributes() }];
+ }
def : Creator<[{
auto extInfo = FunctionType::ExtInfo(noReturn, hasRegParm, regParm,
@@ -338,6 +341,7 @@ let Class = FunctionProtoType in {
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
index 1cd5d8a1f55289..84528660677750 100644
--- a/clang/include/clang/Basic/Attr.td
+++ b/clang/include/clang/Basic/Attr.td
@@ -2439,6 +2439,39 @@ def ArmStreaming : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Documentation = [ArmSmeStreamingDocs];
}
+def ArmStreamingCompatible : TypeAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_streaming_compatible">];
+ let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+ let Documentation = [ArmSmeStreamingCompatibleDocs];
+}
+
+def ArmSharedZA : TypeAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_shared_za">];
+ let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+ let Documentation = [ArmSmeSharedZADocs];
+}
+
+def ArmPreservesZA : TypeAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_preserves_za">];
+ let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
+ let Documentation = [ArmSmePreservesZADocs];
+}
+
+def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_locally_streaming">];
+ let Subjects = SubjectList<[Function], ErrorDiag>;
+ let Documentation = [ArmSmeLocallyStreamingDocs];
+}
+
+def ArmNewZA : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
+ let Spellings = [RegularKeyword<"__arm_new_za">];
+ let Subjects = SubjectList<[Function], ErrorDiag>;
+ let Documentation = [ArmSmeNewZADocs];
+}
+def : MutualExclusions<[ArmNewZA, ArmSharedZA]>;
+def : MutualExclusions<[ArmNewZA, ArmPreservesZA]>;
+
+
def Pure : InheritableAttr {
let Spellings = [GCC<"pure">];
let Documentation = [Undocumented];
diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td
index fb7e2ebb0bf2ae..5bda379df3a488 100644
--- a/clang/include/clang/Basic/AttrDocs.td
+++ b/clang/include/clang/Basic/AttrDocs.td
@@ -6581,40 +6581,147 @@ Requirements on Development Tools - Engineering Specification Documentation
}];
}
-def ArmSmeStreamingDocs : Documentation {
- let Category = DocCatType;
+def DocCatArmSmeAttributes : DocumentationCategory<"AArch64 SME Attributes"> {
let Content = [{
-.. Note:: This attribute has not been implemented yet, but once it is
- implemented, it will behave as described below.
+Clang supports a number of AArch64-specific attributes to manage state
+added by the Scalable Matrix Extension (SME). This state includes the
+runtime mode that the processor is in (e.g. non-streaming or streaming)
+as well as the state of the ``ZA`` Matrix Storage.
-The ``__arm_streaming`` keyword is only available on AArch64 targets.
-It applies to function types and specifies that the function has a
-"streaming interface". This means that:
+The attributes come in the form of type- and declaration attributes:
-* the function requires the Scalable Matrix Extension (SME)
+* The SME declaration attributes can appear anywhere that a standard
+ ``[[...]]`` declaration attribute can appear.
-* the function must be entered in streaming mode (that is, with PSTATE.SM
- set to 1)
+* The SME type attributes apply only to prototyped functions and can appear
+ anywhere that a standard ``[[...]]`` type attribute can appear. The SME
+ type attributes do not apply to functions having a K&R-style
+ unprototyped function type.
-* the function must return in streaming mode
-
-* the function does not have a K&R-style unprototyped function type.
+See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_
+for more details about the features related to the SME extension.
See `Procedure Call Standard for the Arm® 64-bit Architecture (AArch64)
<https://github.com/ARM-software/abi-aa>`_ for more details about
-streaming-interface functions.
+streaming-interface functions and shared/private-ZA interface functions.
+ }];
+}
+
+def ArmSmeStreamingDocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_streaming`` keyword applies to prototyped function types and specifies
+that the function has a "streaming interface". This means that:
+
+* the function requires that the processor implements the Scalable Matrix
+ Extension (SME).
+
+* the function must be entered in streaming mode (that is, with PSTATE.SM
+ set to 1)
+
+* the function must return in streaming mode
Clang manages PSTATE.SM automatically; it is not the source code's
-responsibility to do this. For example, if a normal non-streaming
+responsibility to do this. For example, if a non-streaming
function calls an ``__arm_streaming`` function, Clang generates code
that switches into streaming mode before calling the function and
switches back to non-streaming mode on return.
+ }];
+}
+
+def ArmSmeStreamingCompatibleDocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_streaming_compatible`` keyword applies to prototyped function types and
+specifies that the function has a “streaming compatible interface”. This
+means that:
-``__arm_streaming`` can appear anywhere that a standard ``[[...]]`` type
-attribute can appear.
+* the function may be entered in either non-streaming mode (PSTATE.SM=0) or
+ in streaming mode (PSTATE.SM=1).
-See `Arm C Language Extensions <https://github.com/ARM-software/acle>`_
-for more details about this extension, and for other related SME features.
+* the function must return in the same mode as it was entered.
+
+* the code executed in the function is compatible with either mode.
+
+Clang manages PSTATE.SM automatically; it is not the source code's
+responsibility to do this. Clang will ensure that the generated code in
+streaming-compatible functions is valid in either mode (PSTATE.SM=0 or
+PSTATE.SM=1). For example, if an ``__arm_streaming_compatible`` function calls a
+non-streaming function, Clang generates code to temporarily switch out of streaming
+mode before calling the function and switch back to streaming-mode on return if
+``PSTATE.SM`` is ``1`` on entry of the caller. If ``PSTATE.SM`` is ``0`` on
+entry to the ``__arm_streaming_compatible`` function, the call will be executed
+without changing modes.
+ }];
+}
+
+def ArmSmeSharedZADocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_shared_za`` keyword applies to prototyped function types and specifies
+that the function shares SME's matrix storage (ZA) with its caller. This
+means that:
+
+* the function requires that the processor implements the Scalable Matrix
+ Extension (SME).
+
+* the function enters with ZA in an active state.
+
+* the function returns with ZA in an active state.
+ }];
+}
+
+def ArmSmePreservesZADocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_preserves_za`` keyword applies to prototyped function types and
+specifies that the function does not modify ZA state.
+ }];
+}
+
+
+def ArmSmeLocallyStreamingDocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_locally_streaming`` keyword applies to function declarations
+and specifies that all the statements in the function are executed in
+streaming mode. This means that:
+
+* the function requires that the target processor implements the Scalable Matrix
+ Extension (SME).
+
+* the program automatically puts the machine into streaming mode before
+ executing the statements and automatically restores the previous mode
+ afterwards.
+
+Clang manages PSTATE.SM automatically; it is not the source code's
+responsibility to do this. For example, Clang will emit code to enable
+streaming mode at the start of the function, and disable streaming mode
+at the end of the function.
+ }];
+}
+
+def ArmSmeNewZADocs : Documentation {
+ let Category = DocCatArmSmeAttributes;
+ let Content = [{
+The ``__arm_new_za`` keyword applies to function declarations and specifies
+that the function will be set up with a fresh ZA context.
+
+This means that:
+
+* the function requires that the target processor implements the Scalable Matrix
+ Extension (SME).
+
+* the function will commit any lazily saved ZA data.
+
+* the function will create a new ZA context and enable PSTATE.ZA.
+
+* the function will disable PSTATE.ZA (by setting it to 0) before returning.
+
+For ``__arm_new_za`` functions Clang will set up the ZA context automatically
+on entry to the function, and disable it before returning. For example, if ZA is
+in a dormant state Clang will generate the code to commit a lazy-save and set up
+a new ZA state before executing user code.
}];
}
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index ae9ccdc8ab701e..e6b1dc948fc6bd 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2035,6 +2035,10 @@ def err_
diff erent_return_type_for_overriding_virtual_function : Error<
"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
diff erent 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
diff erent calling convention attributes "
"%
diff {($) than the function it overrides (which has calling convention $)|"
@@ -3621,6 +3625,8 @@ def err_attribute_vecreturn_only_vector_member : Error<
"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, which has
diff erent 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
index 4cd58ba071283a..2b96369b76fb46 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -7086,6 +7086,16 @@ class Sema final {
NestedNameSpecInfo &IdInfo,
bool EnteringContext);
+ /// The kind of conversion to check for. Either all attributes must match exactly,
+ /// or the converted type may add/drop '__arm_preserves_za'.
+ enum class AArch64SMECallConversionKind {
+ MatchExactly,
+ MayAddPreservesZA,
+ MayDropPreservesZA,
+ };
+ bool IsInvalidSMECallConversion(QualType FromType, QualType ToType,
+ AArch64SMECallConversionKind C);
+
/// 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
index 8af6e296a20efb..b3fd6fc3d823e2 100644
--- a/clang/lib/AST/Type.cpp
+++ b/clang/lib/AST/Type.cpp
@@ -3391,12 +3391,19 @@ FunctionProtoType::FunctionProtoType(QualType result, ArrayRef<QualType> params,
argSlot[i] = params[i];
}
+ // Propagate the SME ACLE attributes.
+ if (epi.AArch64SMEAttributes != SME_NormalFunction) {
+ auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
+ assert(epi.AArch64SMEAttributes <= SME_AttributeMask &&
+ "Not enough bits to encode SME attributes");
+ ExtraBits.AArch64SMEAttributes = epi.AArch64SMEAttributes;
+ }
+
// Fill in the exception type array if present.
if (getExceptionSpecType() == EST_Dynamic) {
auto &ExtraBits = *getTrailingObjects<FunctionTypeExtraBitfields>();
size_t NumExceptions = epi.ExceptionSpec.Exceptions.size();
- assert(NumExceptions <= UINT16_MAX &&
- "Not enough bits to encode exceptions");
+ assert(NumExceptions <= 1023 && "Not enough bits to encode exceptions");
ExtraBits.NumExceptionType = NumExceptions;
assert(hasExtraBitfields() && "missing trailing extra bitfields!");
@@ -3553,8 +3560,11 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
// This is followed by an optional "consumed argument" section of the
// same length as the first type sequence:
// bool*
- // Finally, we have the ext info and trailing return type flag:
- // int bool
+ // This is followed by the ext info:
+ // int
+ // Finally we have a trailing return type flag (bool)
+ // combined with AArch64 SME Attributes, to save space:
+ // int
//
// There is no ambiguity between the consumed arguments and an empty EH
// spec because of the leading 'bool' which unambiguously indicates
@@ -3587,8 +3597,9 @@ void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID, QualType Result,
for (unsigned i = 0; i != NumParams; ++i)
ID.AddInteger(epi.ExtParameterInfos[i].getOpaqueValue());
}
+
epi.ExtInfo.Profile(ID);
- ID.AddBoolean(epi.HasTrailingReturn);
+ ID.AddInteger((epi.AArch64SMEAttributes << 1) | epi.HasTrailingReturn);
}
void FunctionProtoType::Profile(llvm::FoldingSetNodeID &ID,
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index 1b62f6630928c1..eb69d0bb8755b4 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -938,6 +938,15 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
FunctionType::ExtInfo Info = T->getExtInfo();
+ if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask))
+ OS << " __arm_streaming_compatible";
+ if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask))
+ OS << " __arm_streaming";
+ if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask))
+ OS << " __arm_shared_za";
+ if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateZAPreservedMask))
+ OS << " __arm_preserves_za";
+
printFunctionAfter(Info, OS);
if (!T->getMethodQuals().empty())
@@ -1772,6 +1781,18 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
OS << "__arm_streaming";
return;
}
+ if (T->getAttrKind() == attr::ArmStreamingCompatible) {
+ OS << "__arm_streaming_compatible";
+ return;
+ }
+ if (T->getAttrKind() == attr::ArmSharedZA) {
+ OS << "__arm_shared_za";
+ return;
+ }
+ if (T->getAttrKind() == attr::ArmPreservesZA) {
+ OS << "__arm_preserves_za";
+ return;
+ }
OS << " __attribute__((";
switch (T->getAttrKind()) {
@@ -1814,6 +1835,9 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::AnnotateType:
case attr::WebAssemblyFuncref:
case attr::ArmStreaming:
+ case attr::ArmStreamingCompatible:
+ case attr::ArmSharedZA:
+ case attr::ArmPreservesZA:
llvm_unreachable("This attribute should have been handled already");
case attr::NSReturnsRetained:
diff --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index 43f5ae8d68c664..f15b5ae0568afd 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -1761,6 +1761,15 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
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_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,
@@ -2406,6 +2415,12 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,
if (TargetDecl->hasAttr<CUDAGlobalAttr>() &&
getLangOpts().OffloadUniformBlock)
FuncAttrs.addAttribute("uniform-work-group-size", "true");
+
+ if (TargetDecl->hasAttr<ArmLocallyStreamingAttr>())
+ FuncAttrs.addAttribute("aarch64_pstate_sm_body");
+
+ if (TargetDecl->hasAttr<ArmNewZAAttr>())
+ FuncAttrs.addAttribute("aarch64_pstate_za_new");
}
// Attach "no-builtins" attributes to:
diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp
index e7cbc748b7a38b..b78ddd6b4425e6 100644
--- a/clang/lib/CodeGen/CodeGenModule.cpp
+++ b/clang/lib/CodeGen/CodeGenModule.cpp
@@ -2293,6 +2293,14 @@ void CodeGenModule::SetLLVMFunctionAttributesForDefinition(const Decl *D,
return;
}
+ // Handle SME attributes that apply to function definitions,
+ // rather than to function prototypes.
+ if (D->hasAttr<ArmLocallyStreamingAttr>())
+ B.addAttribute("aarch64_pstate_sm_body");
+
+ if (D->hasAttr<ArmNewZAAttr>())
+ B.addAttribute("aarch64_pstate_za_new");
+
// 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
index a24856b64ec92b..390d09547f2a58 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3770,6 +3770,16 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
}
}
+ // It is not permitted to redeclare an SME function with
diff erent SME
+ // attributes.
+ if (IsInvalidSMECallConversion(Old->getType(), New->getType(),
+ AArch64SMECallConversionKind::MatchExactly)) {
+ 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
index f71e663bca315f..d0c293b65b7f74 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -5354,9 +5354,6 @@ bool Sema::CheckCallingConvAttr(const ParsedAttr &Attrs, CallingConv &CC,
case ParsedAttr::AT_AArch64SVEPcs:
CC = CC_AArch64SVEPCS;
break;
- case ParsedAttr::AT_ArmStreaming:
- CC = CC_C; // FIXME: placeholder until real SME support is added.
- break;
case ParsedAttr::AT_AMDGPUKernelCall:
CC = CC_AMDGPUKernelCall;
break;
@@ -8731,6 +8728,28 @@ static bool MustDelayAttributeArguments(const ParsedAttr &AL) {
return false;
}
+
+static void handleArmNewZaAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
+ if (auto *FPT = dyn_cast<FunctionProtoType>(D->getFunctionType())) {
+ if (FPT->getAArch64SMEAttributes() &
+ FunctionType::SME_PStateZASharedMask) {
+ S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)
+ << AL << "'__arm_shared_za'" << true;
+ AL.setInvalid();
+ }
+ if (FPT->getAArch64SMEAttributes() &
+ FunctionType::SME_PStateZAPreservedMask) {
+ S.Diag(AL.getLoc(), diag::err_attributes_are_not_compatible)
+ << AL << "'__arm_preserves_za'" << true;
+ AL.setInvalid();
+ }
+ if (AL.isInvalid())
+ return;
+ }
+
+ handleSimpleAttribute<ArmNewZAAttr>(S, D, AL);
+}
+
/// ProcessDeclAttribute - Apply the specific attribute to the specified decl if
/// the attribute applies to decls. If the attribute is a type attribute, just
/// silently ignore it if a GNU attribute.
@@ -9486,6 +9505,14 @@ ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, const ParsedAttr &AL,
handleArmBuiltinAliasAttr(S, D, AL);
break;
+ case ParsedAttr::AT_ArmLocallyStreaming:
+ handleSimpleAttribute<ArmLocallyStreamingAttr>(S, D, AL);
+ break;
+
+ case ParsedAttr::AT_ArmNewZA:
+ handleArmNewZaAttr(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
index 2c2c9dc8147437..9b95af722d398f 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -18049,6 +18049,16 @@ bool Sema::CheckOverridingFunctionAttributes(const CXXMethodDecl *New,
}
}
+ // SME attributes must match when overriding a function declaration.
+ if (IsInvalidSMECallConversion(
+ Old->getType(), New->getType(),
+ AArch64SMECallConversionKind::MayAddPreservesZA)) {
+ 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<CodeSegAttr>();
const auto *NewCSA = New->getAttr<CodeSegAttr>();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index c1c6a34e8bc84f..2b5fdc9d08aa44 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -9679,6 +9679,40 @@ ExprResult Sema::ActOnConditionalOp(SourceLocation QuestionLoc,
ColonLoc, result, VK, OK);
}
+// Check that the SME attributes for PSTATE.ZA and PSTATE.SM are compatible.
+bool Sema::IsInvalidSMECallConversion(QualType FromType, QualType ToType,
+ AArch64SMECallConversionKind C) {
+ unsigned FromAttributes = 0, ToAttributes = 0;
+ if (const auto *FromFn =
+ dyn_cast<FunctionProtoType>(Context.getCanonicalType(FromType)))
+ FromAttributes =
+ FromFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask;
+ if (const auto *ToFn =
+ dyn_cast<FunctionProtoType>(Context.getCanonicalType(ToType)))
+ ToAttributes =
+ ToFn->getAArch64SMEAttributes() & FunctionType::SME_AttributeMask;
+
+ if (FromAttributes == ToAttributes)
+ return false;
+
+ // If the '__arm_preserves_za' is the only
diff erence between the types,
+ // check whether we're allowed to add or remove it.
+ if ((FromAttributes ^ ToAttributes) ==
+ FunctionType::SME_PStateZAPreservedMask) {
+ switch (C) {
+ case AArch64SMECallConversionKind::MatchExactly:
+ return true;
+ case AArch64SMECallConversionKind::MayAddPreservesZA:
+ return !(ToAttributes & FunctionType::SME_PStateZAPreservedMask);
+ case AArch64SMECallConversionKind::MayDropPreservesZA:
+ return !(FromAttributes & FunctionType::SME_PStateZAPreservedMask);
+ }
+ }
+
+ // There has been a mismatch of attributes
+ return true;
+}
+
// 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.
@@ -9845,6 +9879,10 @@ checkPointerTypesForAssignment(Sema &S, QualType LHSType, QualType RHSType,
return Sema::IncompatibleFunctionPointer;
if (IsInvalidCmseNSCallConversion(S, ltrans, rtrans))
return Sema::IncompatibleFunctionPointer;
+ if (S.IsInvalidSMECallConversion(
+ rtrans, ltrans,
+ Sema::AArch64SMECallConversionKind::MayDropPreservesZA))
+ return Sema::IncompatibleFunctionPointer;
return ConvTy;
}
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ad47b81b9dfadf..3b14fb6b66e450 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -1687,6 +1687,26 @@ bool Sema::IsFunctionConversion(QualType FromType, QualType ToType,
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<FunctionProtoType>(FromFn)) {
+ FunctionProtoType::ExtProtoInfo ExtInfo = FromFPT->getExtProtoInfo();
+ if (ExtInfo.AArch64SMEAttributes &
+ FunctionType::SME_PStateZAPreservedMask) {
+ unsigned ToFlags = 0;
+ if (const auto *ToFPT = dyn_cast<FunctionProtoType>(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<FunctionType>();
+ Changed = true;
+ }
+ }
+ }
+
// Drop 'noexcept' if not present in target type.
if (const auto *FromFPT = dyn_cast<FunctionProtoType>(FromFn)) {
const auto *ToFPT = cast<FunctionProtoType>(ToFn);
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 0aa691d24171f3..2ccad19e066d39 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -128,7 +128,6 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
case ParsedAttr::AT_VectorCall: \
case ParsedAttr::AT_AArch64VectorPcs: \
case ParsedAttr::AT_AArch64SVEPcs: \
- case ParsedAttr::AT_ArmStreaming: \
case ParsedAttr::AT_AMDGPUKernelCall: \
case ParsedAttr::AT_MSABI: \
case ParsedAttr::AT_SysVABI: \
@@ -143,6 +142,10 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
case ParsedAttr::AT_NoReturn: \
case ParsedAttr::AT_Regparm: \
case ParsedAttr::AT_CmseNSCall: \
+ case ParsedAttr::AT_ArmStreaming: \
+ case ParsedAttr::AT_ArmStreamingCompatible: \
+ case ParsedAttr::AT_ArmSharedZA: \
+ case ParsedAttr::AT_ArmPreservesZA: \
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
case ParsedAttr::AT_AnyX86NoCfCheck: \
CALLING_CONV_ATTRS_CASELIST
@@ -7772,6 +7775,26 @@ static Attr *getCCTypeAttr(ASTContext &Ctx, ParsedAttr &Attr) {
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
+ << (OtherAttr->isRegularKeywordAttribute() ||
+ Attr.isRegularKeywordAttribute());
+ 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,
@@ -7901,6 +7924,55 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
return true;
}
+ if (attr.getKind() == ParsedAttr::AT_ArmStreaming ||
+ attr.getKind() == ParsedAttr::AT_ArmStreamingCompatible ||
+ 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 auto *FnTy = unwrapped.get()->getAs<FunctionProtoType>();
+ if (!FnTy) {
+ // SME ACLE attributes are not supported on K&R-style unprototyped C
+ // functions.
+ S.Diag(attr.getLoc(), diag::warn_attribute_wrong_decl_type) <<
+ attr << attr.isRegularKeywordAttribute() << ExpectedFunctionWithProtoType;
+ 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_ArmSharedZA:
+ EPI.setArmSMEAttribute(FunctionType::SME_PStateZASharedMask);
+ break;
+ case ParsedAttr::AT_ArmPreservesZA:
+ 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<FunctionType>());
+ 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
index 00000000000000..6581fd4baba9e4
--- /dev/null
+++ b/clang/test/AST/ast-dump-sme-attributes.cpp
@@ -0,0 +1,66 @@
+// 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/ <undeserialized declarations>//" -e "s/ imported//" \
+// RUN: | FileCheck --strict-whitespace %s
+
+struct Foo {
+// CHECK: |-CXXRecordDecl {{.*}} implicit struct Foo
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming 'void () __arm_streaming'
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_streaming_compatible 'void () __arm_streaming_compatible'
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_locally_streaming 'void ()'
+// CHECK-NEXT: | `-ArmLocallyStreamingAttr
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_shared_za 'void () __arm_shared_za'
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_new_za 'void ()'
+// CHECK-NEXT: | `-ArmNewZAAttr
+// CHECK-NEXT: |-CXXMethodDecl {{.*}} f_preserves_za 'void () __arm_preserves_za'
+ void f_streaming() __arm_streaming;
+ void f_streaming_compatible() __arm_streaming_compatible;
+ __arm_locally_streaming void f_locally_streaming();
+ void f_shared_za() __arm_shared_za;
+ __arm_new_za void f_new_za();
+ void f_preserves_za() __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) __arm_streaming const' inline
+// CHECK: | | |-CXXConversionDecl {{.*}} implicit constexpr operator int (*)(int) __arm_streaming 'int (*() const noexcept)(int) __arm_streaming' inline
+// CHECK: | | |-CXXMethodDecl {{.*}} implicit __invoke 'int (int) __arm_streaming' static inline
+// CHECK: `-ReturnStmt
+// CHECK: `-CXXOperatorCallExpr
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int (*)(int) __arm_streaming const' <FunctionToPointerDecay>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int (int) __arm_streaming const' lvalue CXXMethod {{.*}} 'operator()' 'int (int) __arm_streaming const'
+ int test_lambda(int x) {
+ auto F = [](int x) __arm_streaming { return x; };
+ return F(x);
+ }
+
+// CHECK: |-TypedefDecl {{.*}} referenced s_ptrty 'void (*)(int, int) __arm_streaming'
+// CHECK-NEXT: | `-PointerType {{.*}} 'void (*)(int, int) __arm_streaming'
+// CHECK-NEXT: | `-ParenType {{.*}} 'void (int, int) __arm_streaming' sugar
+// CHECK-NEXT: | `-FunctionProtoType {{.*}} 'void (int, int) __arm_streaming' cdecl
+ typedef void (*s_ptrty) (int, int) __arm_streaming;
+
+// CHECK: `-CXXMethodDecl {{.*}} test_streaming_ptrty 'void (s_ptrty, int, int)' implicit-inline
+// CHECK-NEXT: |-ParmVarDecl {{.*}} used f 's_ptrty':'void (*)(int, int) __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) __arm_streaming' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} 's_ptrty':'void (*)(int, int) __arm_streaming' lvalue ParmVar {{.*}} 'f' 's_ptrty':'void (*)(int, int) __arm_streaming'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} 'int' lvalue ParmVar {{.*}} 'x' 'int'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} 'int' <LValueToRValue>
+// 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
index 00000000000000..52937495484d04
--- /dev/null
+++ b/clang/test/CodeGen/aarch64-sme-intrinsics/aarch64-sme-attrs.cpp
@@ -0,0 +1,303 @@
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme \
+// RUN: -S -disable-O0-optnone -Werror -emit-llvm -o - %s \
+// RUN: | opt -S -passes=mem2reg \
+// RUN: | opt -S -passes=inline \
+// RUN: | FileCheck %s
+
+extern "C" {
+
+extern int normal_callee();
+
+// == FUNCTION DECLARATIONS ==
+
+int streaming_decl(void) __arm_streaming;
+int streaming_compatible_decl(void) __arm_streaming_compatible;
+int shared_za_decl(void) __arm_shared_za;
+int preserves_za_decl(void) __arm_preserves_za;
+int private_za_decl(void);
+
+// == FUNCTION DEFINITIONS ==
+
+// CHECK-LABEL: @streaming_caller()
+// CHECK-SAME: #[[SM_ENABLED:[0-9]+]]
+// CHECK: call i32 @normal_callee()
+//
+ int streaming_caller() __arm_streaming {
+ 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]+]]
+//
+ int streaming_callee() __arm_streaming {
+ 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()
+//
+ int streaming_compatible_caller() __arm_streaming_compatible {
+ return normal_callee();
+}
+
+// CHECK-LABEL: @streaming_compatible_callee()
+// CHECK-SAME: #[[SM_COMPATIBLE]]
+// CHECK: call i32 @streaming_compatible_decl() #[[SM_COMPATIBLE_CALL:[0-9]+]]
+//
+ int streaming_compatible_callee() __arm_streaming_compatible {
+ 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()
+//
+__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]+]]
+//
+__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()
+//
+ int shared_za_caller() __arm_shared_za {
+ return normal_callee();
+}
+
+// CHECK-LABEL: @shared_za_callee()
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call i32 @shared_za_decl() #[[ZA_SHARED_CALL:[0-9]+]]
+//
+ int shared_za_callee() __arm_shared_za {
+ 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()
+//
+ int preserves_za_caller() __arm_preserves_za {
+ return normal_callee();
+}
+
+// CHECK-LABEL: @preserves_za_callee()
+// CHECK-SAME: #[[ZA_PRESERVED]]
+// CHECK: call i32 @preserves_za_decl() #[[ZA_PRESERVED_CALL:[0-9]+]]
+//
+ int preserves_za_callee() __arm_preserves_za {
+ 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()
+//
+__arm_new_za int new_za_caller() {
+ return normal_callee();
+}
+
+// CHECK-LABEL: @new_za_callee()
+// CHECK-SAME: #[[ZA_NEW]]
+// CHECK: call i32 @private_za_decl()
+//
+__arm_new_za int new_za_callee() {
+ return private_za_decl();
+}
+
+// CHECK: declare i32 @private_za_decl()
+
+
+// Ensure that the attributes are correctly propagated to function types
+// and also to callsites.
+typedef void (*s_ptrty) (int, int) __arm_streaming;
+typedef void (*sc_ptrty) (int, int) __arm_streaming_compatible;
+typedef void (*sz_ptrty) (int, int) __arm_shared_za;
+typedef void (*pz_ptrty) (int, int) __arm_preserves_za;
+
+// 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_shared_za(
+// CHECK-SAME: #[[ZA_SHARED]]
+// CHECK: call void [[F:%.*]](i32 noundef [[X:%.*]], i32 noundef [[Y:%.*]]) #[[ZA_SHARED_CALL]]
+//
+void test_shared_za(sz_ptrty f, int x, int y) __arm_shared_za { 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 test_preserved_za(pz_ptrty f, int x, int y) __arm_shared_za { 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
diff erent 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 @_Z20same_type_streaming1v() #[[SM_ENABLED_CALL]]
+// CHECK: call void @_Z20same_type_streaming2v() #[[SM_ENABLED_CALL]]
+// CHECK: ret void
+// CHECK: }
+// CHECK: declare void @_Z10streaming1v() #[[SM_ENABLED_DECL]]
+// CHECK: declare void @_Z10streaming2v() #[[SM_ENABLED_DECL]]
+// CHECK: declare void @_Z20same_type_streaming1v() #[[SM_ENABLED_DECL]]
+// CHECK: declare void @_Z20same_type_streaming2v() #[[SM_ENABLED_DECL]]
+void streaming1(void) __arm_streaming;
+void streaming2() __arm_streaming;
+decltype(streaming1) same_type_streaming1;
+decltype(streaming2) same_type_streaming2;
+void test_same_type_streaming() {
+ streaming1();
+ streaming2();
+ same_type_streaming1();
+ same_type_streaming2();
+}
+
+//
+// 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 overloadedfn(int x) __arm_streaming { 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) __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 <typename Ty>
+Ty template_functy(Ty x) __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]]
+__arm_locally_streaming void locally_streaming_inherit();
+void locally_streaming_inherit() {
+ streaming_decl();
+}
+
+// Test that the attributes are propagated properly to calls
+// when using a variadic template as indirection.
+__attribute__((always_inline))
+int call() { return 0; }
+
+template <typename T, typename... Other>
+__attribute__((always_inline))
+int call(T f, Other... other) {
+ return f() + call(other...);
+}
+
+// CHECK: {{.*}} @_Z22test_variadic_templatev(
+// CHECK: call {{.*}} i32 @normal_callee() #[[NOUNWIND_CALL:[0-9]+]]
+// CHECK-NEXT: call {{.*}} i32 @streaming_decl() #[[NOUNWIND_SM_ENABLED_CALL:[0-9]+]]
+// CHECK-NEXT: call {{.*}} i32 @streaming_compatible_decl() #[[NOUNWIND_SM_COMPATIBLE_CALL:[0-9]+]]
+// CHECK-NEXT: call {{.*}} i32 @shared_za_decl() #[[NOUNWIND_ZA_SHARED_CALL:[0-9]+]]
+// CHECK-NEXT: call {{.*}} i32 @preserves_za_decl() #[[NOUNWIND_ZA_PRESERVED_CALL:[0-9]+]]
+// CHECK-NEXT: add nsw
+// CHECK-NEXT: add nsw
+// CHECK-NEXT: add nsw
+// CHECK-NEXT: add nsw
+// CHECK-NEXT: ret
+int test_variadic_template() {
+ return call(normal_callee,
+ streaming_decl,
+ streaming_compatible_decl,
+ shared_za_decl,
+ preserves_za_decl);
+}
+
+// CHECK: attributes #[[SM_ENABLED]] = { mustprogress noinline nounwind "aarch64_pstate_sm_enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[NORMAL_DECL]] = { "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[SM_ENABLED_DECL]] = { "aarch64_pstate_sm_enabled" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[SM_COMPATIBLE]] = { mustprogress noinline nounwind "aarch64_pstate_sm_compatible" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[SM_COMPATIBLE_DECL]] = { "aarch64_pstate_sm_compatible" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[SM_BODY]] = { mustprogress noinline nounwind "aarch64_pstate_sm_body" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[ZA_SHARED]] = { mustprogress noinline nounwind "aarch64_pstate_za_shared" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[ZA_SHARED_DECL]] = { "aarch64_pstate_za_shared" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[ZA_PRESERVED]] = { mustprogress noinline nounwind "aarch64_pstate_za_preserved" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[ZA_PRESERVED_DECL]] = { "aarch64_pstate_za_preserved" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[ZA_NEW]] = { mustprogress noinline nounwind "aarch64_pstate_za_new" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
+// CHECK: attributes #[[NORMAL_DEF]] = { mustprogress noinline nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+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 #[[NOUNWIND_CALL]] = { nounwind }
+// CHECK: attributes #[[NOUNWIND_SM_ENABLED_CALL]] = { nounwind "aarch64_pstate_sm_enabled" }
+// CHECK: attributes #[[NOUNWIND_SM_COMPATIBLE_CALL]] = { nounwind "aarch64_pstate_sm_compatible" }
+// CHECK: attributes #[[NOUNWIND_ZA_SHARED_CALL]] = { nounwind "aarch64_pstate_za_shared" }
+// CHECK: attributes #[[NOUNWIND_ZA_PRESERVED_CALL]] = { nounwind "aarch64_pstate_za_preserved" }
+
diff --git a/clang/test/Modules/aarch64-sme-keywords.cppm b/clang/test/Modules/aarch64-sme-keywords.cppm
new file mode 100644
index 00000000000000..6784aaa01d2198
--- /dev/null
+++ b/clang/test/Modules/aarch64-sme-keywords.cppm
@@ -0,0 +1,65 @@
+// REQUIRES: aarch64-registered-target
+//
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+//
+// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme %t/A.cppm -emit-module-interface -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 -triple aarch64 -target-feature +sme -fprebuilt-module-path=%t -I%t %t/Use.cpp -emit-llvm
+// RUN: cat %t/Use.ll | FileCheck %s
+
+//--- A.cppm
+module;
+export module A;
+
+export void f_streaming(void) __arm_streaming { }
+export void f_streaming_compatible(void) __arm_streaming_compatible { }
+export void f_shared_za(void) __arm_shared_za { }
+export void f_preserves_za(void) __arm_preserves_za { }
+
+//--- Use.cpp
+// expected-no-diagnostics
+import A;
+
+// CHECK: define dso_local void @_Z18f_shared_za_callerv() #[[SHARED_ZA_DEF:[0-9]+]] {
+// CHECK: entry:
+// CHECK: call void @_ZW1A11f_shared_zav() #[[SHARED_ZA_USE:[0-9]+]]
+// CHECK: call void @_ZW1A14f_preserves_zav() #[[PRESERVES_ZA_USE:[0-9]+]]
+// CHECK: ret void
+// CHECK: }
+//
+// CHECK:declare void @_ZW1A11f_shared_zav() #[[SHARED_ZA_DECL:[0-9]+]]
+//
+// CHECK:declare void @_ZW1A14f_preserves_zav() #[[PRESERVES_ZA_DECL:[0-9]+]]
+//
+// CHECK:; Function Attrs: mustprogress noinline nounwind optnone
+// CHECK:define dso_local void @_Z21f_nonstreaming_callerv() #[[NORMAL_DEF:[0-9]+]] {
+// CHECK:entry:
+// CHECK: call void @_ZW1A11f_streamingv() #[[STREAMING_USE:[0-9]+]]
+// CHECK: call void @_ZW1A22f_streaming_compatiblev() #[[STREAMING_COMPATIBLE_USE:[0-9]+]]
+// CHECK: ret void
+// CHECK:}
+//
+// CHECK:declare void @_ZW1A11f_streamingv() #[[STREAMING_DECL:[0-9]+]]
+//
+// CHECK:declare void @_ZW1A22f_streaming_compatiblev() #[[STREAMING_COMPATIBLE_DECL:[0-9]+]]
+//
+// CHECK-DAG: attributes #[[SHARED_ZA_DEF]] = {{{.*}} "aarch64_pstate_za_shared" {{.*}}}
+// CHECK-DAG: attributes #[[SHARED_ZA_DECL]] = {{{.*}} "aarch64_pstate_za_shared" {{.*}}}
+// CHECK-DAG: attributes #[[PRESERVES_ZA_DECL]] = {{{.*}} "aarch64_pstate_za_preserved" {{.*}}}
+// CHECK-DAG: attributes #[[NORMAL_DEF]] = {{{.*}}}
+// CHECK-DAG: attributes #[[STREAMING_DECL]] = {{{.*}} "aarch64_pstate_sm_enabled" {{.*}}}
+// CHECK-DAG: attributes #[[STREAMING_COMPATIBLE_DECL]] = {{{.*}} "aarch64_pstate_sm_compatible" {{.*}}}
+// CHECK-DAG: attributes #[[SHARED_ZA_USE]] = { "aarch64_pstate_za_shared" }
+// CHECK-DAG: attributes #[[PRESERVES_ZA_USE]] = { "aarch64_pstate_za_preserved" }
+// CHECK-DAG: attributes #[[STREAMING_USE]] = { "aarch64_pstate_sm_enabled" }
+// CHECK-DAG: attributes #[[STREAMING_COMPATIBLE_USE]] = { "aarch64_pstate_sm_compatible" }
+
+void f_shared_za_caller(void) __arm_shared_za {
+ f_shared_za();
+ f_preserves_za();
+}
+
+void f_nonstreaming_caller(void) {
+ f_streaming();
+ f_streaming_compatible();
+}
diff --git a/clang/test/Sema/aarch64-sme-func-attrs.c b/clang/test/Sema/aarch64-sme-func-attrs.c
new file mode 100644
index 00000000000000..71140a9ebc61eb
--- /dev/null
+++ b/clang/test/Sema/aarch64-sme-func-attrs.c
@@ -0,0 +1,308 @@
+// 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
+
+void sme_arm_streaming(void) __arm_streaming;
+void sme_arm_streaming_compatible(void) __arm_streaming_compatible;
+
+__arm_new_za void sme_arm_new_za(void) {}
+void sme_arm_shared_za(void) __arm_shared_za;
+void sme_arm_preserves_za(void) __arm_preserves_za;
+
+__arm_new_za void sme_arm_streaming_new_za(void) __arm_streaming {}
+void sme_arm_streaming_shared_za(void) __arm_streaming __arm_shared_za;
+void sme_arm_streaming_preserves_za(void) __arm_streaming __arm_preserves_za;
+
+__arm_new_za void sme_arm_sc_new_za(void) __arm_streaming_compatible {}
+void sme_arm_sc_shared_za(void) __arm_streaming_compatible __arm_shared_za;
+void sme_arm_sc_preserves_za(void) __arm_streaming_compatible __arm_preserves_za;
+
+void sme_arm_shared_preserves_za(void) __arm_shared_za __arm_preserves_za;
+
+__arm_locally_streaming void sme_arm_locally_streaming(void) { }
+__arm_locally_streaming void sme_arm_streaming_and_locally_streaming(void) __arm_streaming { }
+__arm_locally_streaming void sme_arm_streaming_and_streaming_compatible(void) __arm_streaming_compatible { }
+
+__arm_locally_streaming __arm_new_za void sme_arm_ls_new_za(void) { }
+__arm_locally_streaming void sme_arm_ls_shared_za(void) __arm_shared_za { }
+__arm_locally_streaming void sme_arm_ls_preserves_za(void) __arm_preserves_za { }
+
+// Valid attributes on function pointers
+
+void streaming_ptr(void) __arm_streaming;
+typedef void (*fptrty1) (void) __arm_streaming;
+fptrty1 call_streaming_func() { return streaming_ptr; }
+
+void streaming_compatible_ptr(void) __arm_streaming_compatible;
+typedef void (*fptrty2) (void) __arm_streaming_compatible;
+fptrty2 call_sc_func() { return streaming_compatible_ptr; }
+
+void shared_za_ptr(void) __arm_shared_za;
+typedef void (*fptrty3) (void) __arm_shared_za;
+fptrty3 call_shared_za_func() { return shared_za_ptr; }
+
+void preserves_za_ptr(void) __arm_preserves_za;
+typedef void (*fptrty4) (void) __arm_preserves_za;
+fptrty4 call_preserve_za_func() { return preserves_za_ptr; }
+
+void shared_preserves_za_ptr(void) __arm_shared_za __arm_preserves_za;
+typedef void (*fptrty5) (void) __arm_shared_za __arm_preserves_za;
+fptrty5 call_shared_preserve_za_func() { return shared_preserves_za_ptr; }
+
+typedef void (*fptrty6) (void);
+fptrty6 cast_nza_func_to_normal() { return sme_arm_new_za; }
+fptrty6 cast_ls_func_to_normal() { return sme_arm_locally_streaming; }
+
+// Invalid attributes
+
+// expected-cpp-error at +4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-cpp-note at +3 {{conflicting attribute is here}}
+// expected-error at +2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-note at +1 {{conflicting attribute is here}}
+void streaming_mode(void) __arm_streaming __arm_streaming_compatible;
+
+// expected-cpp-error at +4 {{'__arm_streaming' and '__arm_streaming_compatible' are not compatible}}
+// expected-cpp-note at +3 {{conflicting attribute is here}}
+// expected-error at +2 {{'__arm_streaming' and '__arm_streaming_compatible' are not compatible}}
+// expected-note at +1 {{conflicting attribute is here}}
+void streaming_compatible(void) __arm_streaming_compatible __arm_streaming;
+
+// expected-cpp-error at +2 {{'__arm_new_za' and '__arm_shared_za' are not compatible}}
+// expected-error at +1 {{'__arm_new_za' and '__arm_shared_za' are not compatible}}
+__arm_new_za void new_shared_za(void) __arm_shared_za {}
+
+// expected-cpp-error at +2 {{'__arm_new_za' and '__arm_preserves_za' are not compatible}}
+// expected-error at +1 {{'__arm_new_za' and '__arm_preserves_za' are not compatible}}
+__arm_new_za void new_preserves_za(void) __arm_preserves_za {}
+
+// Invalid attributes on function pointers
+
+// expected-cpp-error at +4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-cpp-note at +3 {{conflicting attribute is here}}
+// expected-error at +2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-note at +1 {{conflicting attribute is here}}
+void streaming_ptr_invalid(void) __arm_streaming __arm_streaming_compatible;
+// expected-cpp-error at +4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-cpp-note at +3 {{conflicting attribute is here}}
+// expected-error at +2 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
+// expected-note at +1 {{conflicting attribute is here}}
+typedef void (*fptrty7) (void) __arm_streaming __arm_streaming_compatible;
+fptrty7 invalid_streaming_func() { return streaming_ptr_invalid; }
+
+// expected-warning at +2 {{'__arm_streaming' only applies to non-K&R-style functions}}
+// expected-error at +1 {{'__arm_streaming' only applies to function types; type here is 'void ()'}}
+void function_no_prototype() __arm_streaming;
+
+//
+// Check for incorrect conversions of function pointers with the attributes
+//
+
+typedef void (*n_ptrty) (void);
+typedef void (*s_ptrty) (void) __arm_streaming;
+s_ptrty return_valid_streaming_fptr(s_ptrty f) { return f; }
+
+// expected-cpp-error at +2 {{cannot initialize return object of type 's_ptrty' (aka 'void (*)() __arm_streaming') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-error at +1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 's_ptrty' (aka 'void (*)(void) __arm_streaming')}}
+s_ptrty return_invalid_fptr_streaming_normal(n_ptrty f) { return f; }
+// expected-cpp-error at +2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __arm_streaming')}}
+// expected-error at +1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __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 at +2 {{cannot initialize return object of type 'nonproto_n_ptrty' (aka 'void (*)()') with an lvalue of type 's_ptrty' (aka 'void (*)() __arm_streaming')}}
+// expected-error at +1 {{incompatible function pointer types returning 's_ptrty' (aka 'void (*)(void) __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 void (*sc_ptrty) (void) __arm_streaming_compatible;
+sc_ptrty return_valid_streaming_compatible_fptr(sc_ptrty f) { return f; }
+
+// expected-cpp-error at +2 {{cannot initialize return object of type 'sc_ptrty' (aka 'void (*)() __arm_streaming_compatible') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-error at +1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sc_ptrty' (aka 'void (*)(void) __arm_streaming_compatible')}}
+sc_ptrty return_invalid_fptr_streaming_compatible_normal(n_ptrty f) { return f; }
+// expected-cpp-error at +2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sc_ptrty' (aka 'void (*)() __arm_streaming_compatible')}}
+// expected-error at +1 {{incompatible function pointer types returning 'sc_ptrty' (aka 'void (*)(void) __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 void (*sz_ptrty) (void) __arm_shared_za;
+sz_ptrty return_valid_shared_za_fptr(sz_ptrty f) { return f; }
+
+
+// expected-cpp-error at +2 {{cannot initialize return object of type 'sz_ptrty' (aka 'void (*)() __arm_shared_za') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-error at +1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'sz_ptrty' (aka 'void (*)(void) __arm_shared_za')}}
+sz_ptrty return_invalid_fptr_shared_za_normal(n_ptrty f) { return f; }
+// expected-cpp-error at +2 {{cannot initialize return object of type 'n_ptrty' (aka 'void (*)()') with an lvalue of type 'sz_ptrty' (aka 'void (*)() __arm_shared_za')}}
+// expected-error at +1 {{incompatible function pointer types returning 'sz_ptrty' (aka 'void (*)(void) __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 void (*pz_ptrty) (void) __arm_preserves_za;
+pz_ptrty return_valid_preserves_za_fptr(pz_ptrty f) { return f; }
+
+// expected-cpp-error at +2 {{cannot initialize return object of type 'pz_ptrty' (aka 'void (*)() __arm_preserves_za') with an lvalue of type 'n_ptrty' (aka 'void (*)()')}}
+// expected-error at +1 {{incompatible function pointer types returning 'n_ptrty' (aka 'void (*)(void)') from a function with result type 'pz_ptrty' (aka 'void (*)(void) __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 <typename T> T templated(T x) __arm_streaming { return x; }
+template <> int templated<int>(int x) __arm_streaming { return x + 1; }
+template <> float templated<float>(float x) __arm_streaming { return x + 2; }
+// expected-cpp-error at +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 at -4 {{candidate template ignored: could not match 'short (short) __arm_streaming' against 'short (short)'}}
+template short templated<short>(short);
+#endif
+
+// Conflicting attributes on redeclarations
+
+// expected-error at +5 {{function declared 'void (void) __arm_streaming_compatible' was previously declared 'void (void) __arm_streaming', which has
diff erent SME function attributes}}
+// expected-note at +3 {{previous declaration is here}}
+// expected-cpp-error at +3 {{function declared 'void () __arm_streaming_compatible' was previously declared 'void () __arm_streaming', which has
diff erent SME function attributes}}
+// expected-cpp-note at +1 {{previous declaration is here}}
+void redecl(void) __arm_streaming;
+void redecl(void) __arm_streaming_compatible { }
+
+// expected-error at +5 {{function declared 'void (void) __arm_shared_za' was previously declared 'void (void) __arm_shared_za __arm_preserves_za', which has
diff erent SME function attributes}}
+// expected-note at +3 {{previous declaration is here}}
+// expected-cpp-error at +3 {{function declared 'void () __arm_shared_za' was previously declared 'void () __arm_shared_za __arm_preserves_za', which has
diff erent SME function attributes}}
+// expected-cpp-note at +1 {{previous declaration is here}}
+void redecl_preserve_za(void) __arm_shared_za __arm_preserves_za;;
+void redecl_preserve_za(void) __arm_shared_za {}
+
+// expected-error at +5 {{function declared 'void (void) __arm_shared_za __arm_preserves_za' was previously declared 'void (void) __arm_shared_za', which has
diff erent SME function attributes}}
+// expected-note at +3 {{previous declaration is here}}
+// expected-cpp-error at +3 {{function declared 'void () __arm_shared_za __arm_preserves_za' was previously declared 'void () __arm_shared_za', which has
diff erent SME function attributes}}
+// expected-cpp-note at +1 {{previous declaration is here}}
+void redecl_nopreserve_za(void) __arm_shared_za;
+void redecl_nopreserve_za(void) __arm_shared_za __arm_preserves_za {}
+
+#ifdef __cplusplus
+struct S {
+ virtual void shared_za_memberfn(void) __arm_shared_za;
+};
+
+struct S2 : public S {
+// expected-cpp-error at +2 {{virtual function 'shared_za_memberfn' has
diff erent attributes ('void ()') than the function it overrides (which has 'void () __arm_shared_za')}}
+// expected-cpp-note at -5 {{overridden virtual function is here}}
+ __arm_new_za void shared_za_memberfn(void) override {}
+};
+
+// The '__arm_preserves_za' property cannot be dropped when overriding a virtual
+// function. It is however fine for the overriding function to be '__arm_preserves_za'
+// even though the function that it overrides is not.
+
+struct S_PreservesZA {
+ virtual void memberfn(void) __arm_preserves_za;
+};
+
+struct S_Drop_PreservesZA : S_PreservesZA {
+// expected-cpp-error at +2 {{virtual function 'memberfn' has
diff erent attributes ('void ()') than the function it overrides (which has 'void () __arm_preserves_za')}}
+// expected-cpp-note at -5 {{overridden virtual function is here}}
+ void memberfn(void) override {}
+};
+
+struct S_NoPreservesZA {
+ virtual void memberfn(void);
+};
+struct S_AddPreservesZA : S_NoPreservesZA {
+// This is fine, the overridden function just adds more guarantees.
+ void memberfn(void) __arm_preserves_za override {}
+};
+
+
+// Check that the attribute propagates through template instantiations.
+template <typename Ty>
+struct S3 {
+ static constexpr int value = 0;
+};
+
+template <>
+struct S3<void (*)()> {
+ static constexpr int value = 1;
+};
+
+template <>
+struct S3<void (* __arm_streaming)()> {
+ static constexpr int value = 2;
+};
+
+template <>
+struct S3<void (* __arm_streaming_compatible)()> {
+ static constexpr int value = 4;
+};
+
+template <>
+struct S3<void (* __arm_shared_za)()> {
+ static constexpr int value = 8;
+};
+
+template <>
+struct S3<void (* __arm_preserves_za)()> {
+ static constexpr int value = 16;
+};
+
+void normal_func(void) {}
+void streaming_func(void) __arm_streaming {}
+void streaming_compatible_func(void) __arm_streaming_compatible {}
+void shared_za_func(void) __arm_shared_za {}
+void preserves_za_func(void) __arm_preserves_za {}
+
+static_assert(S3<decltype(+normal_func)>::value == 1, "why are we picking the wrong specialization?");
+static_assert(S3<decltype(+streaming_func)>::value == 2, "why are we picking the wrong specialization?");
+static_assert(S3<decltype(+streaming_compatible_func)>::value == 4, "why are we picking the wrong specialization?");
+static_assert(S3<decltype(+shared_za_func)>::value == 8, "why are we picking the wrong specialization?");
+static_assert(S3<decltype(+preserves_za_func)>::value == 16, "why are we picking the wrong specialization?");
+
+// Also test the attribute is propagated with variadic templates
+constexpr int eval_variadic_template() { return 0; }
+template <typename T, typename... Other>
+constexpr int eval_variadic_template(T f, Other... other) {
+ return S3<decltype(f)>::value + eval_variadic_template(other...);
+}
+static_assert(eval_variadic_template(normal_func, streaming_func,
+ streaming_compatible_func,
+ shared_za_func, preserves_za_func) == 31,
+ "attributes not propagated properly in variadic template");
+
+// Test that the attribute is propagated with template specialization.
+template<typename T> int test_templated_f(T);
+template<> constexpr int test_templated_f<void(*)(void)>(void(*)(void)) { return 1; }
+template<> constexpr int test_templated_f<void(*)(void)__arm_streaming>(void(*)(void)__arm_streaming) { return 2; }
+template<> constexpr int test_templated_f<void(*)(void)__arm_streaming_compatible>(void(*)(void)__arm_streaming_compatible) { return 4; }
+template<> constexpr int test_templated_f<void(*)(void)__arm_shared_za>(void(*)(void)__arm_shared_za) { return 8; }
+template<> constexpr int test_templated_f<void(*)(void)__arm_preserves_za>(void(*)(void)__arm_preserves_za) { return 16; }
+
+static_assert(test_templated_f(&normal_func) == 1, "Instantiated to wrong function");
+static_assert(test_templated_f(&streaming_func) == 2, "Instantiated to wrong function");
+static_assert(test_templated_f(&streaming_compatible_func) == 4, "Instantiated to wrong function");
+static_assert(test_templated_f(&shared_za_func) == 8, "Instantiated to wrong function");
+static_assert(test_templated_f(&preserves_za_func) == 16, "Instantiated to wrong function");
+
+// expected-cpp-error at +2 {{'__arm_streaming' only applies to function types; type here is 'int'}}
+// expected-error at +1 {{'__arm_streaming' only applies to function types; type here is 'int'}}
+int invalid_type_for_attribute __arm_streaming;
+
+// Test overloads
+constexpr int overload(void f(void)) { return 1; }
+constexpr int overload(void f(void) __arm_streaming) { return 2; }
+constexpr int overload(void f(void) __arm_streaming_compatible) { return 4; }
+constexpr int overload(void f(void) __arm_shared_za) { return 8; }
+constexpr int overload(void f(void) __arm_preserves_za) { return 16; }
+static_assert(overload(&normal_func) == 1, "Overloaded to wrong function");
+static_assert(overload(&streaming_func) == 2, "Overloaded to wrong function");
+static_assert(overload(&streaming_compatible_func) == 4, "Overloaded to wrong function");
+static_assert(overload(&shared_za_func) == 8, "Overloaded to wrong function");
+static_assert(overload(&preserves_za_func) == 16, "Overloaded to wrong function");
+
+// Test implicit instantiation
+template <typename T> struct X {
+ static void foo(T) __arm_streaming { }
+};
+constexpr int overload_int(void f(int)) { return 1; }
+constexpr int overload_int(void f(int) __arm_streaming) { return 2; }
+constexpr X<int> *ptr = 0;
+static_assert(overload_int(ptr->foo) == 2, "Overloaded to the wrong function after implicit instantiation");
+
+#endif // ifdef __cplusplus
More information about the cfe-commits
mailing list