Skip to content

Commit

Permalink
[Clang][AArch64] Change SME attributes for shared/new/preserved state. (
Browse files Browse the repository at this point in the history
#76971)

This patch replaces the `__arm_new_za`, `__arm_shared_za` and
`__arm_preserves_za` attributes in favour of:
* `__arm_new("za")`
* `__arm_in("za")`
* `__arm_out("za")`
* `__arm_inout("za")`
* `__arm_preserves("za")`

As described in ARM-software/acle#276.

One change is that `__arm_in/out/inout/preserves(S)` are all mutually
exclusive, whereas previously it was fine to write `__arm_shared_za
__arm_preserves_za`. This case is now represented with `__arm_in("za")`.

The current implementation uses the same LLVM attributes under the hood,
since `__arm_in/out/inout` are all variations of "shared ZA", so can use
the existing `aarch64_pstate_za_shared` attribute in LLVM.

#77941 will add support for the new "zt0" state as introduced
with SME2.
  • Loading branch information
sdesmalen-arm committed Jan 15, 2024
1 parent 06e3abc commit 8e7f073
Show file tree
Hide file tree
Showing 76 changed files with 1,663 additions and 1,358 deletions.
23 changes: 19 additions & 4 deletions clang/include/clang/AST/Type.h
Original file line number Diff line number Diff line change
Expand Up @@ -4036,12 +4036,27 @@ class FunctionType : public Type {
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.

// Describes the value of the state using ArmStateValue.
SME_ZAShift = 2,
SME_ZAMask = 0b111 << SME_ZAShift,

SME_AttributeMask = 0b111'111 // We only support maximum 6 bits because of
// the bitmask in FunctionTypeExtraBitfields.
};

enum ArmStateValue : unsigned {
ARM_None = 0,
ARM_Preserves = 1,
ARM_In = 2,
ARM_Out = 3,
ARM_InOut = 4,
};

static ArmStateValue getArmZAState(unsigned AttrBits) {
return (ArmStateValue)((AttrBits & SME_ZAMask) >> SME_ZAShift);
}

/// 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.
Expand Down
49 changes: 35 additions & 14 deletions clang/include/clang/Basic/Attr.td
Original file line number Diff line number Diff line change
Expand Up @@ -2539,16 +2539,45 @@ def ArmStreamingCompatible : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Documentation = [ArmSmeStreamingCompatibleDocs];
}

def ArmSharedZA : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_shared_za">];
def ArmNew : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_new">];
let Args = [VariadicStringArgument<"NewArgs">];
let Subjects = SubjectList<[Function], ErrorDiag>;
let Documentation = [ArmNewDocs];

let AdditionalMembers = [{
bool isNewZA() const {
return llvm::is_contained(newArgs(), "za");
}
}];
}

def ArmIn : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_in">];
let Args = [VariadicStringArgument<"InArgs">];
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
let Documentation = [ArmInDocs];
}

def ArmOut : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_out">];
let Args = [VariadicStringArgument<"OutArgs">];
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
let Documentation = [ArmOutDocs];
}

def ArmInOut : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_inout">];
let Args = [VariadicStringArgument<"InOutArgs">];
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
let Documentation = [ArmSmeSharedZADocs];
let Documentation = [ArmInOutDocs];
}

def ArmPreservesZA : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_preserves_za">];
def ArmPreserves : TypeAttr, TargetSpecificAttr<TargetAArch64> {
let Spellings = [RegularKeyword<"__arm_preserves">];
let Args = [VariadicStringArgument<"PreserveArgs">];
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
let Documentation = [ArmSmePreservesZADocs];
let Documentation = [ArmPreservesDocs];
}

def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
Expand All @@ -2557,14 +2586,6 @@ def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
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">];
Expand Down
82 changes: 65 additions & 17 deletions clang/include/clang/Basic/AttrDocs.td
Original file line number Diff line number Diff line change
Expand Up @@ -6861,30 +6861,73 @@ without changing modes.
}];
}

def ArmSmeSharedZADocs : Documentation {
def ArmInDocs : 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 ``__arm_in`` keyword applies to prototyped function types and specifies
that the function shares a given state S with its caller. For ``__arm_in``, the
function takes the state S as input and returns with the state S unchanged.

* the function requires that the processor implements the Scalable Matrix
Extension (SME).
The attribute takes string arguments to instruct the compiler which state
is shared. The supported states for S are:

* the function enters with ZA in an active state.
* ``"za"`` for Matrix Storage (requires SME)

* the function returns with ZA in an active state.
The attributes ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` and
``__arm_preserves(S)`` are all mutually exclusive for the same state S.
}];
}

def ArmSmePreservesZADocs : Documentation {
def ArmOutDocs : 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.
The ``__arm_out`` keyword applies to prototyped function types and specifies
that the function shares a given state S with its caller. For ``__arm_out``,
the function ignores the incoming state for S and returns new state for S.

The attribute takes string arguments to instruct the compiler which state
is shared. The supported states for S are:

* ``"za"`` for Matrix Storage (requires SME)

The attributes ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` and
``__arm_preserves(S)`` are all mutually exclusive for the same state S.
}];
}

def ArmInOutDocs : Documentation {
let Category = DocCatArmSmeAttributes;
let Content = [{
The ``__arm_inout`` keyword applies to prototyped function types and specifies
that the function shares a given state S with its caller. For ``__arm_inout``,
the function takes the state S as input and returns new state for S.

The attribute takes string arguments to instruct the compiler which state
is shared. The supported states for S are:

* ``"za"`` for Matrix Storage (requires SME)

The attributes ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` and
``__arm_preserves(S)`` are all mutually exclusive for the same state S.
}];
}

def ArmPreservesDocs : Documentation {
let Category = DocCatArmSmeAttributes;
let Content = [{
The ``__arm_preserves`` keyword applies to prototyped function types and
specifies that the function does not read a given state S and returns
with state S unchanged.

The attribute takes string arguments to instruct the compiler which state
is shared. The supported states for S are:

* ``"za"`` for Matrix Storage (requires SME)

The attributes ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` and
``__arm_preserves(S)`` are all mutually exclusive for the same state S.
}];
}

def ArmSmeLocallyStreamingDocs : Documentation {
let Category = DocCatArmSmeAttributes;
Expand All @@ -6907,13 +6950,18 @@ at the end of the function.
}];
}

def ArmSmeNewZADocs : Documentation {
def ArmNewDocs : 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.
The ``__arm_new`` keyword applies to function declarations and specifies
that the function will create a new scope for state S.

The attribute takes string arguments to instruct the compiler for which state
to create new scope. The supported states for S are:

* ``"za"`` for Matrix Storage (requires SME)

This means that:
For state ``"za"``, this means that:

* the function requires that the target processor implements the Scalable Matrix
Extension (SME).
Expand All @@ -6924,8 +6972,8 @@ This means that:

* 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
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
13 changes: 13 additions & 0 deletions clang/include/clang/Basic/AttributeCommonInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,19 @@ class AttributeCommonInfo {
return SpellingIndex != SpellingNotCalculated;
}
};

inline bool doesKeywordAttributeTakeArgs(tok::TokenKind Kind) {
switch (Kind) {
default:
return false;
#define KEYWORD_ATTRIBUTE(NAME, HASARG) \
case tok::kw_##NAME: \
return HASARG;
#include "clang/Basic/RegularKeywordAttrInfo.inc"
#undef KEYWORD_ATTRIBUTE
}
}

} // namespace clang

#endif // LLVM_CLANG_BASIC_ATTRIBUTECOMMONINFO_H
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ clang_tablegen(AttrSubMatchRulesList.inc -gen-clang-attr-subject-match-rule-list
SOURCE Attr.td
TARGET ClangAttrSubjectMatchRuleList)

clang_tablegen(AttrTokenKinds.inc -gen-clang-attr-token-kinds
clang_tablegen(RegularKeywordAttrInfo.inc -gen-clang-regular-keyword-attr-info
-I ${CMAKE_CURRENT_SOURCE_DIR}/../../
SOURCE Attr.td
TARGET ClangAttrTokenKinds
TARGET ClangRegularKeywordAttrInfo
)

clang_tablegen(AttrHasAttributeImpl.inc -gen-clang-attr-has-attribute-impl
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 @@ -3700,6 +3700,12 @@ def err_sme_definition_using_sm_in_non_sme_target : Error<
"function executed in streaming-SVE mode requires 'sme'">;
def err_sme_definition_using_za_in_non_sme_target : Error<
"function using ZA state requires 'sme'">;
def err_conflicting_attributes_arm_state : Error<
"conflicting attributes for state '%0'">;
def err_unknown_arm_state : Error<
"unknown state '%0'">;
def err_missing_arm_state : Error<
"missing state for %0">;
def err_cconv_change : Error<
"function declared '%0' here was previously declared "
"%select{'%2'|without calling convention}1">;
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/TokenKinds.def
Original file line number Diff line number Diff line change
Expand Up @@ -761,9 +761,9 @@ KEYWORD(__builtin_sycl_unique_stable_name, KEYSYCL)

// Keywords defined by Attr.td.
#ifndef KEYWORD_ATTRIBUTE
#define KEYWORD_ATTRIBUTE(X) KEYWORD(X, KEYALL)
#define KEYWORD_ATTRIBUTE(X, ...) KEYWORD(X, KEYALL)
#endif
#include "clang/Basic/AttrTokenKinds.inc"
#include "clang/Basic/RegularKeywordAttrInfo.inc"

// Clang-specific keywords enabled only in testing.
TESTING_KEYWORD(__unknown_anytype , KEYALL)
Expand Down
4 changes: 2 additions & 2 deletions clang/include/clang/Basic/TokenKinds.h
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,8 @@ bool isPragmaAnnotation(TokenKind K);

inline constexpr bool isRegularKeywordAttribute(TokenKind K) {
return (false
#define KEYWORD_ATTRIBUTE(X) || (K == tok::kw_##X)
#include "clang/Basic/AttrTokenKinds.inc"
#define KEYWORD_ATTRIBUTE(X, ...) || (K == tok::kw_##X)
#include "clang/Basic/RegularKeywordAttrInfo.inc"
);
}

Expand Down
10 changes: 1 addition & 9 deletions clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -7110,15 +7110,7 @@ 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);
bool IsInvalidSMECallConversion(QualType FromType, QualType ToType);

/// The parser has parsed a nested-name-specifier
/// 'template[opt] template-name < template-args >::'.
Expand Down
31 changes: 15 additions & 16 deletions clang/lib/AST/TypePrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -937,15 +937,20 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
OS << ')';

FunctionType::ExtInfo Info = T->getExtInfo();
unsigned SMEBits = T->getAArch64SMEAttributes();

if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask))
if (SMEBits & FunctionType::SME_PStateSMCompatibleMask)
OS << " __arm_streaming_compatible";
if ((T->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask))
if (SMEBits & 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";
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Preserves)
OS << " __arm_preserves(\"za\")";
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_In)
OS << " __arm_in(\"za\")";
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Out)
OS << " __arm_out(\"za\")";
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_InOut)
OS << " __arm_inout(\"za\")";

printFunctionAfter(Info, OS);

Expand Down Expand Up @@ -1788,14 +1793,6 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
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()) {
Expand Down Expand Up @@ -1839,8 +1836,10 @@ void TypePrinter::printAttributedAfter(const AttributedType *T,
case attr::WebAssemblyFuncref:
case attr::ArmStreaming:
case attr::ArmStreamingCompatible:
case attr::ArmSharedZA:
case attr::ArmPreservesZA:
case attr::ArmIn:
case attr::ArmOut:
case attr::ArmInOut:
case attr::ArmPreserves:
llvm_unreachable("This attribute should have been handled already");

case attr::NSReturnsRetained:
Expand Down
18 changes: 11 additions & 7 deletions clang/lib/CodeGen/CGCall.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1767,14 +1767,21 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
FPT->isNothrow())
FuncAttrs.addAttribute(llvm::Attribute::NoUnwind);

if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMEnabledMask)
unsigned SMEBits = FPT->getAArch64SMEAttributes();
if (SMEBits & FunctionType::SME_PStateSMEnabledMask)
FuncAttrs.addAttribute("aarch64_pstate_sm_enabled");
if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateSMCompatibleMask)
if (SMEBits & FunctionType::SME_PStateSMCompatibleMask)
FuncAttrs.addAttribute("aarch64_pstate_sm_compatible");
if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZASharedMask)

// ZA
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Out ||
FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_InOut)
FuncAttrs.addAttribute("aarch64_pstate_za_shared");
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Preserves ||
FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_In) {
FuncAttrs.addAttribute("aarch64_pstate_za_shared");
if (FPT->getAArch64SMEAttributes() & FunctionType::SME_PStateZAPreservedMask)
FuncAttrs.addAttribute("aarch64_pstate_za_preserved");
}
}

static void AddAttributesFromAssumes(llvm::AttrBuilder &FuncAttrs,
Expand Down Expand Up @@ -2446,9 +2453,6 @@ void CodeGenModule::ConstructAttributeList(StringRef Name,

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:
Expand Down

0 comments on commit 8e7f073

Please sign in to comment.