Skip to content

Commit

Permalink
[Clang][AArch64] Add/implement ACLE keywords for SME.
Browse files Browse the repository at this point in the history
This patch adds all the language-level function keywords defined in:

  ARM-software/acle#188 (merged)
  ARM-software/acle#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
  • Loading branch information
sdesmalen-arm committed Aug 8, 2023
1 parent 24c91d4 commit 28b5f30
Show file tree
Hide file tree
Showing 20 changed files with 1,212 additions and 34 deletions.
53 changes: 47 additions & 6 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -3968,14 +3968,33 @@ 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.
struct alignas(void *) FunctionTypeExtraBitfields {
/// 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:
Expand Down Expand Up @@ -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);
Expand All @@ -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;
}
};

Expand Down Expand Up @@ -4301,6 +4332,7 @@ class FunctionProtoType final
EPI.TypeQuals = getMethodQuals();
EPI.RefQualifier = getRefQualifier();
EPI.ExtParameterInfos = getExtParameterInfosOrNull();
EPI.AArch64SMEAttributes = getAArch64SMEAttributes();
return EPI;
}

Expand Down Expand Up @@ -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())
Expand Down
4 changes: 4 additions & 0 deletions clang/include/clang/AST/TypeProperties.td
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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);
}]>;
}
Expand Down
33 changes: 33 additions & 0 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand Down
145 changes: 126 additions & 19 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -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.
}];
}

Expand Down
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -2035,6 +2035,10 @@ def err_different_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 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 $)|"
Expand Down Expand Up @@ -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 different SME function attributes">;
def err_cconv_change : Error<
"function declared '%0' here was previously declared "
"%select{'%2'|without calling convention}1">;
Expand Down
10 changes: 10 additions & 0 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 >::'.
///
Expand Down
Loading

0 comments on commit 28b5f30

Please sign in to comment.