Skip to content

Commit

Permalink
[Clang][AArch64] Emit 'unimplemented' diagnostic for SME (llvm#80295)
Browse files Browse the repository at this point in the history
When a function F has ZA and ZT0 state, calls another function G that 
only shares ZT0 state with its caller, F will have to save ZA before
the call to G, and restore it afterwards (rather than setting up a
lazy-sve).

This is not yet implemented in LLVM and does not result in a 
compile-time error either. So instead of silently generating incorrect
code, it's better to emit an error saying this is not yet implemented.
  • Loading branch information
sdesmalen-arm authored Feb 2, 2024
1 parent 4cf2ed4 commit 319f4c0
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 29 deletions.
6 changes: 6 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -3711,6 +3711,12 @@ def err_sme_za_call_no_za_state : Error<
"call to a shared ZA function requires the caller to have ZA state">;
def err_sme_zt0_call_no_zt0_state : Error<
"call to a shared ZT0 function requires the caller to have ZT0 state">;
def err_sme_unimplemented_za_save_restore : Error<
"call to a function that shares state other than 'za' from a "
"function that has live 'za' state requires a spill/fill of ZA, which is not yet "
"implemented">;
def note_sme_use_preserves_za : Note<
"add '__arm_preserves(\"za\")' to the callee if it preserves ZA">;
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<
Expand Down
50 changes: 23 additions & 27 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7513,47 +7513,43 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}

// If the callee uses AArch64 SME ZA state but the caller doesn't define
// any, then this is an error.
FunctionType::ArmStateValue ArmZAState =
FunctionType::ArmStateValue CalleeArmZAState =
FunctionType::getArmZAState(ExtInfo.AArch64SMEAttributes);
if (ArmZAState != FunctionType::ARM_None) {
FunctionType::ArmStateValue CalleeArmZT0State =
FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
if (CalleeArmZAState != FunctionType::ARM_None ||
CalleeArmZT0State != FunctionType::ARM_None) {
bool CallerHasZAState = false;
bool CallerHasZT0State = false;
if (const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
auto *Attr = CallerFD->getAttr<ArmNewAttr>();
if (Attr && Attr->isNewZA())
CallerHasZAState = true;
else if (const auto *FPT =
CallerFD->getType()->getAs<FunctionProtoType>())
CallerHasZAState = FunctionType::getArmZAState(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
}

if (!CallerHasZAState)
Diag(Loc, diag::err_sme_za_call_no_za_state);
}

// If the callee uses AArch64 SME ZT0 state but the caller doesn't define
// any, then this is an error.
FunctionType::ArmStateValue ArmZT0State =
FunctionType::getArmZT0State(ExtInfo.AArch64SMEAttributes);
if (ArmZT0State != FunctionType::ARM_None) {
bool CallerHasZT0State = false;
if (const auto *CallerFD = dyn_cast<FunctionDecl>(CurContext)) {
auto *Attr = CallerFD->getAttr<ArmNewAttr>();
if (Attr && Attr->isNewZT0())
CallerHasZT0State = true;
else if (const auto *FPT =
CallerFD->getType()->getAs<FunctionProtoType>())
CallerHasZT0State =
if (const auto *FPT = CallerFD->getType()->getAs<FunctionProtoType>()) {
CallerHasZAState |=
FunctionType::getArmZAState(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
CallerHasZT0State |=
FunctionType::getArmZT0State(
FPT->getExtProtoInfo().AArch64SMEAttributes) !=
FunctionType::ARM_None;
}
}

if (!CallerHasZT0State)
if (CalleeArmZAState != FunctionType::ARM_None && !CallerHasZAState)
Diag(Loc, diag::err_sme_za_call_no_za_state);

if (CalleeArmZT0State != FunctionType::ARM_None && !CallerHasZT0State)
Diag(Loc, diag::err_sme_zt0_call_no_zt0_state);

if (CallerHasZAState && CalleeArmZAState == FunctionType::ARM_None &&
CalleeArmZT0State != FunctionType::ARM_None) {
Diag(Loc, diag::err_sme_unimplemented_za_save_restore);
Diag(Loc, diag::note_sme_use_preserves_za);
}
}
}

Expand Down
13 changes: 11 additions & 2 deletions clang/test/Sema/aarch64-sme-func-attrs.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// 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
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme2 -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sme2 -fsyntax-only -verify=expected-cpp -x c++ %s

// Valid attributes

Expand Down Expand Up @@ -445,3 +445,12 @@ void conflicting_state_attrs_preserves_out_zt0(void) __arm_preserves("zt0") __ar
// expected-cpp-error@+2 {{conflicting attributes for state 'zt0'}}
// expected-error@+1 {{conflicting attributes for state 'zt0'}}
void conflicting_state_attrs_preserves_inout_zt0(void) __arm_preserves("zt0") __arm_inout("zt0");

// Test that we get a diagnostic for unimplemented case.
void unimplemented_spill_fill_za(void (*share_zt0_only)(void) __arm_inout("zt0")) __arm_inout("za", "zt0") {
// expected-cpp-error@+4 {{call to a function that shares state other than 'za' from a function that has live 'za' state requires a spill/fill of ZA, which is not yet implemented}}
// expected-cpp-note@+3 {{add '__arm_preserves("za")' to the callee if it preserves ZA}}
// expected-error@+2 {{call to a function that shares state other than 'za' from a function that has live 'za' state requires a spill/fill of ZA, which is not yet implemented}}
// expected-note@+1 {{add '__arm_preserves("za")' to the callee if it preserves ZA}}
share_zt0_only();
}

0 comments on commit 319f4c0

Please sign in to comment.