Skip to content

Commit b4ce29a

Browse files
[AArch64][Clang] Add support for __arm_agnostic("sme_za_state") (#121788)
This adds support for parsing the attribute and codegen to map it to "aarch64_za_state_agnostic" LLVM IR attribute. This attribute is described in the Arm C Language Extensions (ACLE) document: https://github.com/ARM-software/acle/blob/main/main/acle.md#__arm_agnostic
1 parent 5c0aa31 commit b4ce29a

File tree

12 files changed

+169
-11
lines changed

12 files changed

+169
-11
lines changed

clang/include/clang/AST/Type.h

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4593,9 +4593,14 @@ class FunctionType : public Type {
45934593
SME_ZT0Shift = 5,
45944594
SME_ZT0Mask = 0b111 << SME_ZT0Shift,
45954595

4596+
// A bit to tell whether a function is agnostic about sme ZA state.
4597+
SME_AgnosticZAStateShift = 8,
4598+
SME_AgnosticZAStateMask = 1 << SME_AgnosticZAStateShift,
4599+
45964600
SME_AttributeMask =
4597-
0b111'111'11 // We can't support more than 8 bits because of
4598-
// the bitmask in FunctionTypeExtraBitfields.
4601+
0b1'111'111'11 // We can't support more than 9 bits because of
4602+
// the bitmask in FunctionTypeArmAttributes
4603+
// and ExtProtoInfo.
45994604
};
46004605

46014606
enum ArmStateValue : unsigned {
@@ -4620,7 +4625,7 @@ class FunctionType : public Type {
46204625
struct alignas(void *) FunctionTypeArmAttributes {
46214626
/// Any AArch64 SME ACLE type attributes that need to be propagated
46224627
/// on declarations and function pointers.
4623-
unsigned AArch64SMEAttributes : 8;
4628+
unsigned AArch64SMEAttributes : 9;
46244629

46254630
FunctionTypeArmAttributes() : AArch64SMEAttributes(SME_NormalFunction) {}
46264631
};
@@ -5188,7 +5193,7 @@ class FunctionProtoType final
51885193
FunctionType::ExtInfo ExtInfo;
51895194
unsigned Variadic : 1;
51905195
unsigned HasTrailingReturn : 1;
5191-
unsigned AArch64SMEAttributes : 8;
5196+
unsigned AArch64SMEAttributes : 9;
51925197
Qualifiers TypeQuals;
51935198
RefQualifierKind RefQualifier = RQ_None;
51945199
ExceptionSpecInfo ExceptionSpec;

clang/include/clang/Basic/Attr.td

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2877,6 +2877,13 @@ def ArmPreserves : TypeAttr, TargetSpecificAttr<TargetAArch64> {
28772877
let Documentation = [ArmPreservesDocs];
28782878
}
28792879

2880+
def ArmAgnostic : TypeAttr, TargetSpecificAttr<TargetAArch64> {
2881+
let Spellings = [RegularKeyword<"__arm_agnostic">];
2882+
let Args = [VariadicStringArgument<"AgnosticArgs">];
2883+
let Subjects = SubjectList<[HasFunctionProto], ErrorDiag>;
2884+
let Documentation = [ArmAgnosticDocs];
2885+
}
2886+
28802887
def ArmLocallyStreaming : InheritableAttr, TargetSpecificAttr<TargetAArch64> {
28812888
let Spellings = [RegularKeyword<"__arm_locally_streaming">];
28822889
let Subjects = SubjectList<[Function], ErrorDiag>;

clang/include/clang/Basic/AttrDocs.td

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7635,6 +7635,32 @@ The attributes ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` and
76357635
}];
76367636
}
76377637

7638+
def ArmAgnosticDocs : Documentation {
7639+
let Category = DocCatArmSmeAttributes;
7640+
let Content = [{
7641+
The ``__arm_agnostic`` keyword applies to prototyped function types and
7642+
affects the function's calling convention for a given state S. This
7643+
attribute allows the user to describe a function that preserves S, without
7644+
requiring the function to share S with its callers and without making
7645+
the assumption that S exists.
7646+
7647+
If a function has the ``__arm_agnostic(S)`` attribute and calls a function
7648+
without this attribute, then the function's object code will contain code
7649+
to preserve state S. Otherwise, the function's object code will be the same
7650+
as if it did not have the attribute.
7651+
7652+
The attribute takes string arguments to describe state S. The supported
7653+
states are:
7654+
7655+
* ``"sme_za_state"`` for state enabled by PSTATE.ZA, such as ZA and ZT0.
7656+
7657+
The attribute ``__arm_agnostic("sme_za_state")`` cannot be used in conjunction
7658+
with ``__arm_in(S)``, ``__arm_out(S)``, ``__arm_inout(S)`` or
7659+
``__arm_preserves(S)`` where state S describes state enabled by PSTATE.ZA,
7660+
such as "za" or "zt0".
7661+
}];
7662+
}
7663+
76387664
def ArmSmeLocallyStreamingDocs : Documentation {
76397665
let Category = DocCatArmSmeAttributes;
76407666
let Content = [{

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3835,6 +3835,9 @@ def err_sme_unimplemented_za_save_restore : Error<
38353835
"call to a function that shares state other than 'za' from a "
38363836
"function that has live 'za' state requires a spill/fill of ZA, which is not yet "
38373837
"implemented">;
3838+
def err_sme_unsupported_agnostic_new : Error<
3839+
"__arm_agnostic(\"sme_za_state\") is not supported together with "
3840+
"__arm_new(\"za\") or __arm_new(\"zt0\")">;
38383841
def note_sme_use_preserves_za : Note<
38393842
"add '__arm_preserves(\"za\")' to the callee if it preserves ZA">;
38403843
def err_sme_definition_using_sm_in_non_sme_target : Error<
@@ -3851,6 +3854,8 @@ def warn_sme_locally_streaming_has_vl_args_returns : Warning<
38513854
"%select{returning|passing}0 a VL-dependent argument %select{from|to}0 a locally streaming function is undefined"
38523855
" behaviour when the streaming and non-streaming vector lengths are different at runtime">,
38533856
InGroup<AArch64SMEAttributes>, DefaultIgnore;
3857+
def err_conflicting_attributes_arm_agnostic : Error<
3858+
"__arm_agnostic(\"sme_za_state\") cannot share ZA state with its caller">;
38543859
def err_conflicting_attributes_arm_state : Error<
38553860
"conflicting attributes for state '%0'">;
38563861
def err_unknown_arm_state : Error<

clang/lib/AST/ItaniumMangle.cpp

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3585,13 +3585,15 @@ void CXXNameMangler::mangleSMEAttrs(unsigned SMEAttrs) {
35853585
else if (SMEAttrs & FunctionType::SME_PStateSMCompatibleMask)
35863586
Bitmask |= AAPCSBitmaskSME::ArmStreamingCompatibleBit;
35873587

3588-
// TODO: Must represent __arm_agnostic("sme_za_state")
3589-
3590-
Bitmask |= encodeAAPCSZAState(FunctionType::getArmZAState(SMEAttrs))
3591-
<< AAPCSBitmaskSME::ZA_Shift;
3588+
if (SMEAttrs & FunctionType::SME_AgnosticZAStateMask)
3589+
Bitmask |= AAPCSBitmaskSME::ArmAgnosticSMEZAStateBit;
3590+
else {
3591+
Bitmask |= encodeAAPCSZAState(FunctionType::getArmZAState(SMEAttrs))
3592+
<< AAPCSBitmaskSME::ZA_Shift;
35923593

3593-
Bitmask |= encodeAAPCSZAState(FunctionType::getArmZT0State(SMEAttrs))
3594-
<< AAPCSBitmaskSME::ZT0_Shift;
3594+
Bitmask |= encodeAAPCSZAState(FunctionType::getArmZT0State(SMEAttrs))
3595+
<< AAPCSBitmaskSME::ZT0_Shift;
3596+
}
35953597

35963598
Out << "Lj" << static_cast<unsigned>(Bitmask) << "EE";
35973599
}

clang/lib/AST/TypePrinter.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,6 +1000,8 @@ void TypePrinter::printFunctionProtoAfter(const FunctionProtoType *T,
10001000
OS << " __arm_streaming_compatible";
10011001
if (SMEBits & FunctionType::SME_PStateSMEnabledMask)
10021002
OS << " __arm_streaming";
1003+
if (SMEBits & FunctionType::SME_AgnosticZAStateMask)
1004+
OS << "__arm_agnostic(\"sme_za_state\")";
10031005
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Preserves)
10041006
OS << " __arm_preserves(\"za\")";
10051007
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_In)

clang/lib/CodeGen/CGCall.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1779,6 +1779,8 @@ static void AddAttributesFromFunctionProtoType(ASTContext &Ctx,
17791779
FuncAttrs.addAttribute("aarch64_pstate_sm_enabled");
17801780
if (SMEBits & FunctionType::SME_PStateSMCompatibleMask)
17811781
FuncAttrs.addAttribute("aarch64_pstate_sm_compatible");
1782+
if (SMEBits & FunctionType::SME_AgnosticZAStateMask)
1783+
FuncAttrs.addAttribute("aarch64_za_state_agnostic");
17821784

17831785
// ZA
17841786
if (FunctionType::getArmZAState(SMEBits) == FunctionType::ARM_Preserves)

clang/lib/Sema/SemaARM.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1337,6 +1337,14 @@ void SemaARM::CheckSMEFunctionDefAttributes(const FunctionDecl *FD) {
13371337
bool UsesZA = Attr && Attr->isNewZA();
13381338
bool UsesZT0 = Attr && Attr->isNewZT0();
13391339

1340+
if (UsesZA || UsesZT0) {
1341+
if (const auto *FPT = FD->getType()->getAs<FunctionProtoType>()) {
1342+
FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
1343+
if (EPI.AArch64SMEAttributes & FunctionType::SME_AgnosticZAStateMask)
1344+
Diag(FD->getLocation(), diag::err_sme_unsupported_agnostic_new);
1345+
}
1346+
}
1347+
13401348
if (FD->hasAttr<ArmLocallyStreamingAttr>()) {
13411349
if (FD->getReturnType()->isSizelessVectorType())
13421350
Diag(FD->getLocation(),

clang/lib/Sema/SemaType.cpp

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ static void diagnoseBadTypeAttribute(Sema &S, const ParsedAttr &attr,
161161
case ParsedAttr::AT_ArmIn: \
162162
case ParsedAttr::AT_ArmOut: \
163163
case ParsedAttr::AT_ArmInOut: \
164+
case ParsedAttr::AT_ArmAgnostic: \
164165
case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: \
165166
case ParsedAttr::AT_AnyX86NoCfCheck: \
166167
CALLING_CONV_ATTRS_CASELIST
@@ -7745,6 +7746,40 @@ static bool checkMutualExclusion(TypeProcessingState &state,
77457746
return true;
77467747
}
77477748

7749+
static bool handleArmAgnosticAttribute(Sema &S,
7750+
FunctionProtoType::ExtProtoInfo &EPI,
7751+
ParsedAttr &Attr) {
7752+
if (!Attr.getNumArgs()) {
7753+
S.Diag(Attr.getLoc(), diag::err_missing_arm_state) << Attr;
7754+
Attr.setInvalid();
7755+
return true;
7756+
}
7757+
7758+
for (unsigned I = 0; I < Attr.getNumArgs(); ++I) {
7759+
StringRef StateName;
7760+
SourceLocation LiteralLoc;
7761+
if (!S.checkStringLiteralArgumentAttr(Attr, I, StateName, &LiteralLoc))
7762+
return true;
7763+
7764+
if (StateName != "sme_za_state") {
7765+
S.Diag(LiteralLoc, diag::err_unknown_arm_state) << StateName;
7766+
Attr.setInvalid();
7767+
return true;
7768+
}
7769+
7770+
if (EPI.AArch64SMEAttributes &
7771+
(FunctionType::SME_ZAMask | FunctionType::SME_ZT0Mask)) {
7772+
S.Diag(Attr.getLoc(), diag::err_conflicting_attributes_arm_agnostic);
7773+
Attr.setInvalid();
7774+
return true;
7775+
}
7776+
7777+
EPI.setArmSMEAttribute(FunctionType::SME_AgnosticZAStateMask);
7778+
}
7779+
7780+
return false;
7781+
}
7782+
77487783
static bool handleArmStateAttribute(Sema &S,
77497784
FunctionProtoType::ExtProtoInfo &EPI,
77507785
ParsedAttr &Attr,
@@ -7775,6 +7810,12 @@ static bool handleArmStateAttribute(Sema &S,
77757810
return true;
77767811
}
77777812

7813+
if (EPI.AArch64SMEAttributes & FunctionType::SME_AgnosticZAStateMask) {
7814+
S.Diag(LiteralLoc, diag::err_conflicting_attributes_arm_agnostic);
7815+
Attr.setInvalid();
7816+
return true;
7817+
}
7818+
77787819
// __arm_in(S), __arm_out(S), __arm_inout(S) and __arm_preserves(S)
77797820
// are all mutually exclusive for the same S, so check if there are
77807821
// conflicting attributes.
@@ -7925,7 +7966,8 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
79257966
attr.getKind() == ParsedAttr::AT_ArmPreserves ||
79267967
attr.getKind() == ParsedAttr::AT_ArmIn ||
79277968
attr.getKind() == ParsedAttr::AT_ArmOut ||
7928-
attr.getKind() == ParsedAttr::AT_ArmInOut) {
7969+
attr.getKind() == ParsedAttr::AT_ArmInOut ||
7970+
attr.getKind() == ParsedAttr::AT_ArmAgnostic) {
79297971
if (S.CheckAttrTarget(attr))
79307972
return true;
79317973

@@ -7976,6 +8018,10 @@ static bool handleFunctionTypeAttr(TypeProcessingState &state, ParsedAttr &attr,
79768018
if (handleArmStateAttribute(S, EPI, attr, FunctionType::ARM_InOut))
79778019
return true;
79788020
break;
8021+
case ParsedAttr::AT_ArmAgnostic:
8022+
if (handleArmAgnosticAttribute(S, EPI, attr))
8023+
return true;
8024+
break;
79798025
default:
79808026
llvm_unreachable("Unsupported attribute");
79818027
}

clang/test/CodeGen/AArch64/sme-intrinsics/aarch64-sme-attrs.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ int streaming_compatible_decl(void) __arm_streaming_compatible;
1515
int shared_za_decl(void) __arm_inout("za");
1616
int preserves_za_decl(void) __arm_preserves("za");
1717
int private_za_decl(void);
18+
int agnostic_za_decl(void) __arm_agnostic("sme_za_state");
1819

1920
// == FUNCTION DEFINITIONS ==
2021

@@ -130,6 +131,27 @@ __arm_new("za") int new_za_callee() {
130131

131132
// CHECK: declare i32 @private_za_decl()
132133

134+
// CHECK-LABEL: @agnostic_za_caller()
135+
// CHECK-SAME: #[[ZA_AGNOSTIC:[0-9]+]]
136+
// CHECK: call i32 @normal_callee()
137+
//
138+
int agnostic_za_caller() __arm_agnostic("sme_za_state") {
139+
return normal_callee();
140+
}
141+
142+
// CHECK-LABEL: @agnostic_za_callee()
143+
// CHECK: call i32 @agnostic_za_decl() #[[ZA_AGNOSTIC_CALL:[0-9]+]]
144+
//
145+
int agnostic_za_callee() {
146+
return agnostic_za_decl();
147+
}
148+
149+
// CHECK-LABEL: @agnostic_za_callee_live_za()
150+
// CHECK: call i32 @agnostic_za_decl() #[[ZA_AGNOSTIC_CALL]]
151+
//
152+
int agnostic_za_callee_live_za() __arm_inout("za") {
153+
return agnostic_za_decl();
154+
}
133155

134156
// Ensure that the attributes are correctly propagated to function types
135157
// and also to callsites.
@@ -289,12 +311,14 @@ int test_variadic_template() __arm_inout("za") {
289311
// CHECK: attributes #[[ZA_PRESERVED]] = { mustprogress noinline nounwind "aarch64_preserves_za" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
290312
// CHECK: attributes #[[ZA_PRESERVED_DECL]] = { "aarch64_preserves_za" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
291313
// CHECK: attributes #[[ZA_NEW]] = { mustprogress noinline nounwind "aarch64_new_za" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
314+
// CHECK: attributes #[[ZA_AGNOSTIC]] = { mustprogress noinline nounwind "aarch64_za_state_agnostic" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
292315
// CHECK: attributes #[[NORMAL_DEF]] = { mustprogress noinline nounwind "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-features"="+bf16,+sme" }
293316
// CHECK: attributes #[[SM_ENABLED_CALL]] = { "aarch64_pstate_sm_enabled" }
294317
// CHECK: attributes #[[SM_COMPATIBLE_CALL]] = { "aarch64_pstate_sm_compatible" }
295318
// CHECK: attributes #[[SM_BODY_CALL]] = { "aarch64_pstate_sm_body" }
296319
// CHECK: attributes #[[ZA_SHARED_CALL]] = { "aarch64_inout_za" }
297320
// CHECK: attributes #[[ZA_PRESERVED_CALL]] = { "aarch64_preserves_za" }
321+
// CHECK: attributes #[[ZA_AGNOSTIC_CALL]] = { "aarch64_za_state_agnostic" }
298322
// CHECK: attributes #[[NOUNWIND_CALL]] = { nounwind }
299323
// CHECK: attributes #[[NOUNWIND_SM_ENABLED_CALL]] = { nounwind "aarch64_pstate_sm_enabled" }
300324
// CHECK: attributes #[[NOUNWIND_SM_COMPATIBLE_CALL]] = { nounwind "aarch64_pstate_sm_compatible" }

clang/test/CodeGenCXX/aarch64-mangle-sme-atts.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,16 @@ __arm_new("zt0") void fn_zt0_out(int (*foo)() __arm_out("zt0")) { foo(); }
4545
// CHECK: define dso_local void @_Z12fn_zt0_inoutP11__SME_ATTRSIFivELj192EE(
4646
__arm_new("zt0") void fn_zt0_inout(int (*foo)() __arm_inout("zt0")) { foo(); }
4747

48+
//
49+
// __arm_agnostic("sme_za_state") Attribute
50+
//
51+
52+
// CHECK: define dso_local void @_Z24fn_sme_za_state_agnosticP11__SME_ATTRSIFvvELj4EE(
53+
void fn_sme_za_state_agnostic(void (*foo)() __arm_agnostic("sme_za_state")) { foo(); }
54+
55+
// CHECK: define dso_local void @_Z34fn_sme_za_state_streaming_agnosticP11__SME_ATTRSIFvvELj5EE(
56+
void fn_sme_za_state_streaming_agnostic(void (*foo)() __arm_streaming __arm_agnostic("sme_za_state")) { foo(); }
57+
4858
//
4959
// Streaming-mode, ZA & ZT0 Attributes
5060
//

clang/test/Sema/aarch64-sme-func-attrs.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ void sme_arm_streaming_compatible(void) __arm_streaming_compatible;
99
__arm_new("za") void sme_arm_new_za(void) {}
1010
void sme_arm_shared_za(void) __arm_inout("za");
1111
void sme_arm_preserves_za(void) __arm_preserves("za");
12+
void sme_arm_agnostic(void) __arm_agnostic("sme_za_state");
1213

1314
__arm_new("za") void sme_arm_streaming_new_za(void) __arm_streaming {}
1415
void sme_arm_streaming_shared_za(void) __arm_streaming __arm_inout("za");
@@ -88,6 +89,26 @@ fptrty7 invalid_streaming_func() { return streaming_ptr_invalid; }
8889
// expected-error@+1 {{'__arm_streaming' only applies to function types; type here is 'void ()'}}
8990
void function_no_prototype() __arm_streaming;
9091

92+
// expected-cpp-error@+2 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
93+
// expected-error@+1 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
94+
void sme_arm_agnostic_shared_za_zt0(void) __arm_agnostic("sme_za_state") __arm_inout("zt0") {}
95+
96+
// expected-cpp-error@+2 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
97+
// expected-error@+1 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
98+
void sme_arm_agnostic_shared_za_za(void) __arm_agnostic("sme_za_state") __arm_inout("za") {}
99+
100+
// expected-cpp-error@+2 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
101+
// expected-error@+1 {{__arm_agnostic("sme_za_state") cannot share ZA state with its caller}}
102+
void sme_arm_agnostic_shared_za_za_rev(void) __arm_inout("za") __arm_agnostic("sme_za_state") {}
103+
104+
// expected-cpp-error@+2 {{__arm_agnostic("sme_za_state") is not supported together with __arm_new("za") or __arm_new("zt0")}}
105+
// expected-error@+1 {{__arm_agnostic("sme_za_state") is not supported together with __arm_new("za") or __arm_new("zt0")}}
106+
__arm_new("zt0") void sme_arm_agnostic_arm_new_zt0(void) __arm_agnostic("sme_za_state") {}
107+
108+
// expected-cpp-error@+2 {{__arm_agnostic("sme_za_state") is not supported together with __arm_new("za") or __arm_new("zt0")}}
109+
// expected-error@+1 {{__arm_agnostic("sme_za_state") is not supported together with __arm_new("za") or __arm_new("zt0")}}
110+
__arm_new("za") void sme_arm_agnostic_arm_new_za(void) __arm_agnostic("sme_za_state") {}
111+
91112
//
92113
// Check for incorrect conversions of function pointers with the attributes
93114
//

0 commit comments

Comments
 (0)