Skip to content

[Clang][AArch64] Warn when calling streaming/non-streaming about vect… #79842

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 14 commits into from
Apr 10, 2024

Conversation

dtemirbulatov
Copy link
Contributor

…or size might be different.

The compiler doesn't know in advance if the streaming and non-streaming vector-lengths are different, so it should be safe to give a warning diagnostic to warn the user about possible undefined behaviour. If the user knows the vector lengths are equal, they can disable the warning separately.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Jan 29, 2024
@llvmbot
Copy link
Member

llvmbot commented Jan 29, 2024

@llvm/pr-subscribers-clang

Author: Dinar Temirbulatov (dtemirbulatov)

Changes

…or size might be different.

The compiler doesn't know in advance if the streaming and non-streaming vector-lengths are different, so it should be safe to give a warning diagnostic to warn the user about possible undefined behaviour. If the user knows the vector lengths are equal, they can disable the warning separately.


Full diff: https://github.com/llvm/llvm-project/pull/79842.diff

3 Files Affected:

  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+24)
  • (modified) clang/lib/Sema/SemaChecking.cpp (+42)
  • (modified) clang/test/Sema/aarch64-sme-func-attrs.c (+66-2)
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 24d32cb87c89e24..37fea5746936c7a 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3717,6 +3717,30 @@ def err_sme_definition_using_za_in_non_sme_target : Error<
   "function using ZA state requires 'sme'">;
 def err_sme_definition_using_zt0_in_non_sme2_target : Error<
   "function using ZT0 state requires 'sme2'">;
+def warn_sme_streaming_caller_pass_args_to_non_streaming : Warning<
+  "streaming caller passes a VL-dependent argument to non-streaming callee, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
+def warn_sme_non_streaming_callee_returns_to_streaming : Warning<
+  "non-streaming callee returns a VL-dependent value to streaming caller, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
+def warn_sme_non_streaming_caller_pass_args_to_streaming : Warning<
+  "non-streaming caller passes a VL-dependent argument to streaming callee, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
+def warn_sme_non_streaming_caller_returns_to_streaming : Warning<
+  "non-streaming callee returns a VL-dependent value to streaming caller, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
+def warn_sme_locally_streaming_has_vl_args : Warning<
+  "non-streaming callee receives a VL-dependent argument and the callee has an arm_locally_streaming attribute, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
+def warn_sme_locally_streaming_returns_vl : Warning<
+  "non-streaming callee returns a VL-dependent value and the callee has an arm_locally_streaming attribute, "
+  "the streaming and non-streaming vector lengths may be different">,
+  InGroup<IgnoredAttributes>;
 def err_conflicting_attributes_arm_state : Error<
   "conflicting attributes for state '%0'">;
 def err_unknown_arm_state : Error<
diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp
index 502b24bcdf8b42b..e668a45c69e5f91 100644
--- a/clang/lib/Sema/SemaChecking.cpp
+++ b/clang/lib/Sema/SemaChecking.cpp
@@ -7480,6 +7480,7 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
     // For variadic functions, we may have more args than parameters.
     // For some K&R functions, we may have less args than parameters.
     const auto N = std::min<unsigned>(Proto->getNumParams(), Args.size());
+    bool AnyScalableArgs = false;
     for (unsigned ArgIdx = 0; ArgIdx < N; ++ArgIdx) {
       // Args[ArgIdx] can be null in malformed code.
       if (const Expr *Arg = Args[ArgIdx]) {
@@ -7493,6 +7494,8 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
           checkAIXMemberAlignment((Arg->getExprLoc()), Arg);
 
         QualType ParamTy = Proto->getParamType(ArgIdx);
+        if (ParamTy->isSizelessVectorType())
+          AnyScalableArgs = true;
         QualType ArgTy = Arg->getType();
         CheckArgAlignment(Arg->getExprLoc(), FDecl, std::to_string(ArgIdx + 1),
                           ArgTy, ParamTy);
@@ -7513,6 +7516,45 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
       }
     }
 
+    auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
+    if (FD && CallerFD && Context.getTargetInfo().hasFeature("sme") &&
+        !FD->getBuiltinID()) {
+      // If the callee has an AArch64 SME __arm_locally_streaming attribute
+      // warn if this function returns VL-based value or pass any such argument,
+      // the streaming and non-streaming vector lengths may be different.
+      ArmStreamingType CalleeFnType = getArmStreamingFnType(FD);
+      ArmStreamingType CallerFnType = getArmStreamingFnType(CallerFD);
+      if (FD->hasAttr<ArmLocallyStreamingAttr>() &&
+          CallerFnType != ArmStreaming) {
+        if (AnyScalableArgs)
+          Diag(Loc, diag::warn_sme_locally_streaming_has_vl_args);
+        if (FD->getReturnType()->isSizelessVectorType())
+          Diag(Loc, diag::warn_sme_locally_streaming_returns_vl);
+      }
+      // If the caller is a non-streaming function and the callee has a
+      // streaming attribute. If it passed any VL-based arguments or return
+      // VL-based value, then warn that the streaming and non-streaming vector
+      // lengths may be different.
+      if (CallerFnType != ArmStreaming) {
+        if (CalleeFnType == ArmStreaming) {
+          if (AnyScalableArgs)
+            Diag(Loc,
+                 diag::warn_sme_non_streaming_caller_pass_args_to_streaming);
+          if (FD->getReturnType()->isSizelessVectorType())
+            Diag(Loc, diag::warn_sme_non_streaming_caller_returns_to_streaming);
+        }
+      } else if (!FD->hasAttr<ArmLocallyStreamingAttr>()) {
+        // If the callee is a non-streaming function and the caller has
+        // streaming attribute. If it passed any VL-based arguments or return
+        // VL-based value, then warn that the streaming and non-streaming vector
+        // lengths may be different.
+        if (AnyScalableArgs)
+          Diag(Loc, diag::warn_sme_streaming_caller_pass_args_to_non_streaming);
+        if (FD->getReturnType()->isSizelessVectorType())
+          Diag(Loc, diag::warn_sme_non_streaming_callee_returns_to_streaming);
+      }
+    }
+
     // If the callee uses AArch64 SME ZA state but the caller doesn't define
     // any, then this is an error.
     FunctionType::ArmStateValue ArmZAState =
diff --git a/clang/test/Sema/aarch64-sme-func-attrs.c b/clang/test/Sema/aarch64-sme-func-attrs.c
index 97409ae7d6040c0..0a8e6e03a94f291 100644
--- a/clang/test/Sema/aarch64-sme-func-attrs.c
+++ b/clang/test/Sema/aarch64-sme-func-attrs.c
@@ -1,5 +1,6 @@
-// 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 +sve -target-feature +sme -fsyntax-only -verify %s
+// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +sme -fsyntax-only -verify=expected-cpp -x c++ %s
+#include <arm_sme.h>
 
 // Valid attributes
 
@@ -48,6 +49,9 @@ 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; }
 
+void sme_arm_streaming_with_vl_args(void) __arm_streaming;
+
+
 // Invalid attributes
 
 // expected-cpp-error@+4 {{'__arm_streaming_compatible' and '__arm_streaming' are not compatible}}
@@ -445,3 +449,63 @@ 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");
+
+void sme_streaming_with_vl_arg(svint32x4_t a) __arm_streaming { }
+
+svint32x4_t sme_streaming_returns_vl(void) __arm_streaming { svint32x4_t r; return r; }
+
+void sme_none_streaming_with_vl_arg(svint32x4_t a) { }
+
+svint32x4_t sme_none_streaming_returns_vl(void) { svint32x4_t r; return r; }
+
+__arm_locally_streaming void sme_locally_streaming_with_vl_arg(svint32x4_t a) { }
+
+__arm_locally_streaming svint32x4_t sme_locally_streaming_returns_vl(void) { svint32x4_t r; return r; }
+
+void sme_none_streaming_calling_streaming_with_vl_args() {
+  svint32x4_t a;
+  // expected-warning@+2 {{non-streaming caller passes a VL-dependent argument to streaming callee, the streaming and non-streaming vector lengths may be different}}
+  // expected-cpp-warning@+1 {{non-streaming caller passes a VL-dependent argument to streaming callee, the streaming and non-streaming vector lengths may be different}}
+  sme_streaming_with_vl_arg(a);
+}
+
+void sme_none_streaming_calling_streaming_with_return_vl() {
+  // expected-warning@+2 {{non-streaming callee returns a VL-dependent value to streaming caller, the streaming and non-streaming vector lengths may be different}}
+  // expected-cpp-warning@+1 {{non-streaming callee returns a VL-dependent value to streaming caller, the streaming and non-streaming vector lengths may be different}}
+  svint32x4_t r = sme_streaming_returns_vl();
+}
+
+void sme_streaming_calling_non_streaming_with_vl_args(void) __arm_streaming {
+  svint32x4_t a;
+  // expected-warning@+2 {{streaming caller passes a VL-dependent argument to non-streaming callee, the streaming and non-streaming vector lengths may be different}}
+  // expected-cpp-warning@+1 {{streaming caller passes a VL-dependent argument to non-streaming callee, the streaming and non-streaming vector lengths may be different}}
+  sme_none_streaming_with_vl_arg(a);
+}
+
+void sme_streaming_calling_non_streaming_with_return_vl(void) __arm_streaming {
+  // expected-warning@+2 {{non-streaming callee returns a VL-dependent value to streaming caller, the streaming and non-streaming vector lengths may be different}}
+  // expected-cpp-warning@+1 {{non-streaming callee returns a VL-dependent value to streaming caller, the streaming and non-streaming vector lengths may be different}}
+  svint32x4_t r = sme_streaming_returns_vl();
+}
+
+void sme_streaming_calling_locally_streaming_with_vl_args(void) __arm_streaming {
+  svint32x4_t a;
+  // expected-1warning@+2 {{non-streaming callee receives a VL-dependent argument and the callee has an arm_locally_streaming attribute, the streaming and non-streaming vector lengths may be different}}
+  // expected-1cpp-warning@+1 {{non-streaming callee receives a VL-dependent argument and the callee has an arm_locally_streaming attribute, the streaming and non-streaming vector lengths may be different}}
+  sme_locally_streaming_with_vl_arg(a);
+}
+
+void sme_streaming_calling_locally_streaming_with_return_vl(void) __arm_streaming {
+  // expected-1warning@+2 {{non-streaming callee returns a VL-dependent value and the callee has an arm_locally_streaming attribute, the streaming and non-streaming vector lengths may be different}}
+  // expected-1cpp-warning@+1 {{non-streaming callee returns a VL-dependent value and the callee has an arm_locally_streaming attribute, the streaming and non-streaming vector lengths may be different}}
+  svint32x4_t r = sme_locally_streaming_returns_vl();
+}
+
+void sme_none_streaming_calling_locally_streaming_with_vl_args(void) __arm_streaming {
+  svint32x4_t a;
+  sme_locally_streaming_with_vl_arg(a);
+}
+
+void sme_none_streaming_calling_locally_streaming_with_return_vl(void) __arm_streaming {
+  svint32x4_t r = sme_locally_streaming_returns_vl();
+}

Comment on lines 7527 to 7532
if (FD->hasAttr<ArmLocallyStreamingAttr>()) {
if (AnyScalableArgs)
Diag(Loc, diag::warn_sme_locally_streaming_has_vl_args);
if (FD->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_locally_streaming_returns_vl);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

__arm_locally_streaming is not a property of a function interface and therefore unrelated to a function call. That's why this code should live somewhere else.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (FD->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_non_streaming_caller_returns_to_streaming);
}
} else if (!FD->hasAttr<ArmLocallyStreamingAttr>()) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not correct. I get a diagnostic for this case:

svint8_t bar1(void) __arm_streaming;
svint8_t foo1() __arm_streaming {
  return bar1();    // incorrectly gives a diagnostic
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// 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 +sve -target-feature +sme -fsyntax-only -verify %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature +sve -target-feature +sme -fsyntax-only -verify=expected-cpp -x c++ %s
#include <arm_sme.h>
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you write this test without including arm_sme.h ?

Copy link
Contributor Author

@dtemirbulatov dtemirbulatov Feb 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, but then I have to define __clang_svint32x4_t.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

def warn_sme_streaming_caller_pass_args_to_non_streaming : Warning<
"streaming caller passes a VL-dependent argument to non-streaming callee, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<IgnoredAttributes>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think 'IgnoredAttributes' is correct.

These diagnostics probably deserve to be in their own group.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why not, There is warn_riscv_repeated_interrupt_attribute also uses IgnoredAttributes.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because the attribute is not ignored.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add new warning group AArch64SMEAttributes.

@@ -7513,6 +7516,38 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}

auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
if (FD && CallerFD && Context.getTargetInfo().hasFeature("sme") &&
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code requires that the function definition is available (if (FD && ...)), but the streaming mode is exposed through the interface, meaning that we shouldn't need FD in order to emit a diagnostic.

For example:

// I would expect a warning here.
void test_n_to_s(
    __SVInt32_t arg, void (*sc)(__SVInt32_t arg) __arm_streaming) {
  sc(arg);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7531 to 7539
if (CallerFnType != ArmStreaming) {
if (CalleeFnType == ArmStreaming) {
if (AnyScalableArgs)
Diag(Loc,
diag::warn_sme_non_streaming_caller_pass_args_to_streaming);
if (FD->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_non_streaming_caller_returns_to_streaming);
}
} else if (CalleeFnType != ArmStreaming) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This logic is not correct, because the following example should not result in a warning.

void sc_f(sv_ty arg) __arm_streaming_compatible;
void test_s_to_sc2(sv_ty arg) __arm_streaming {
  sc_f(arg);
}```

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 3720 to 3735
def warn_sme_streaming_caller_pass_args_to_non_streaming : Warning<
"streaming caller passes a VL-dependent argument to non-streaming callee, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
def warn_sme_non_streaming_callee_returns_to_streaming : Warning<
"non-streaming callee returns a VL-dependent value to streaming caller, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
def warn_sme_non_streaming_caller_pass_args_to_streaming : Warning<
"non-streaming caller passes a VL-dependent argument to streaming callee, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
def warn_sme_non_streaming_caller_returns_to_streaming : Warning<
"non-streaming callee returns a VL-dependent value to streaming caller, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I think we can use a single message for this, e.g. "passing a VL-dependent argument to/from a function that has a different streaming-mode, is undefined behaviour"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 3736 to 3743
def warn_sme_locally_streaming_has_vl_args : Warning<
"non-streaming callee receives a VL-dependent argument and the callee has an arm_locally_streaming attribute, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
def warn_sme_locally_streaming_returns_vl : Warning<
"non-streaming callee returns a VL-dependent value and the callee has an arm_locally_streaming attribute, "
"the streaming and non-streaming vector lengths may be different">,
InGroup<AArch64SMEAttributes>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two can become a single message as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7535 to 7540
if (CallerFnType != ArmStreaming) {
if (CalleeFnType == ArmStreaming) {
if (IsCalleeStreaming) {
if (AnyScalableArgs)
Diag(Loc,
diag::warn_sme_non_streaming_caller_pass_args_to_streaming);
if (FD->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_non_streaming_caller_returns_to_streaming);
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
if (Proto->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think this could be simplified further?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -12239,7 +12239,8 @@ bool Sema::CheckFunctionDeclaration(Scope *S, FunctionDecl *NewFD,

if (UsesSM) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment on line 12232-12233 needs an update.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 12241 to 12412
if (NewFD->getReturnType()->isSizelessVectorType())
Diag(NewFD->getLocation(),
diag::warn_sme_locally_streaming_has_vl_args_returns);
auto *FPT = NewFD->getType()->castAs<FunctionProtoType>();
bool AnyScalableArgs = false;
for (QualType T : FPT->param_types()) {
if (T->isSizelessVectorType()) {
AnyScalableArgs = true;
break;
}
}
if (AnyScalableArgs)
Diag(NewFD->getLocation(),
diag::warn_sme_locally_streaming_has_vl_args_returns);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be written like something along the lines of:

if (NewFD->getReturnType()->isSizelessVectorType() ||
    llvm::any_of(FPT->param_types(), [](QualType T) { return T->isSizelessVectorType(); }))
 Diag(....);

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (NewFD->getReturnType()->isSizelessVectorType())
Diag(NewFD->getLocation(),
diag::warn_sme_locally_streaming_has_vl_args_returns);
auto *FPT = NewFD->getType()->castAs<FunctionProtoType>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is FunctionPrototype correct here? That means the check does not happen on unprototyped functions, such as __arm_locally_streaming void foo() { ... } in C (not C++).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
if (Proto->getReturnType()->isSizelessVectorType())
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this code support the following case:

void test_sc_to_n(sv_ty arg) __arm_streaming_compatible { n(arg); } // expect a diagnostic

?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7522 to 7523
(ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Streaming-compatible and streaming are not the same thing. i.e. if the callee is 'streaming-compatible', IsCalleeStreaming should be false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -3717,6 +3717,14 @@ def err_sme_definition_using_za_in_non_sme_target : Error<
"function using ZA state requires 'sme'">;
def err_sme_definition_using_zt0_in_non_sme2_target : Error<
"function using ZT0 state requires 'sme2'">;
def warn_sme_streaming_pass_return_vl_to_non_streaming : Warning<
"passing a VL-dependent argument to/from a function that has a different"
" streaming-mode, is undefined behaviour">,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it is only undefined behaviour if the streaming VL != non-streaming VL.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// non-compitable streaming attribute. If it passed any VL-based arguments
// or return VL-based value, then warn that the streaming and non-streaming
// vector lengths may be different.
if (CallerFD && Context.getTargetInfo().hasFeature("sme") && !IsBuiltin) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (CallerFD && Context.getTargetInfo().hasFeature("sme") && !IsBuiltin) {
if (CallerFD && !IsBuiltin) {

It is valid to compile the following function without +sme:

void streaming_compatible_fn(svint32_t v, void (*foo)(svint32_t)) __arm_streaming_compatible {
  foo(v);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

It is not.

@@ -3717,6 +3717,16 @@ def err_sme_definition_using_za_in_non_sme_target : Error<
"function using ZA state requires 'sme'">;
def err_sme_definition_using_zt0_in_non_sme2_target : Error<
"function using ZT0 state requires 'sme2'">;
def warn_sme_streaming_pass_return_vl_to_non_streaming : Warning<
"passing a VL-dependent argument to/from a function that has a different"
" streaming-mode, the streaming and non-streaming vector lengths may be"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
" streaming-mode, the streaming and non-streaming vector lengths may be"
" streaming-mode. The streaming and non-streaming vector lengths may be"

Comment on lines 7534 to 7546
if (CallerFnType != ArmStreaming &&
CallerFnType != ArmStreamingCompatible) {
if (IsCalleeStreaming && AnyScalableArgsOrRet)
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
} else if (CallerFnType == ArmStreaming && !IsCalleeStreaming &&
!IsCalleeStreamingCompatible) {
if (AnyScalableArgsOrRet)
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
} else if (CallerFnType == ArmStreamingCompatible) {
if ((IsCalleeStreaming || !IsCalleeStreamingCompatible) &&
AnyScalableArgsOrRet)
Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think you can simplify this code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"passing a VL-dependent argument to/from a function that has a different"
" streaming-mode, the streaming and non-streaming vector lengths may be"
" different">,
InGroup<AArch64SMEAttributes>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These warnings should be disabled by default, as the user may intentionally pass/return scalable vectors, knowing something about the target they're compiling for.

if (DeclIsDefn) {
const auto *Attr = NewFD->getAttr<ArmNewAttr>();
bool UsesSM = NewFD->hasAttr<ArmLocallyStreamingAttr>();
bool UsesZA = Attr && Attr->isNewZA();
bool UsesZT0 = Attr && Attr->isNewZT0();

if (UsesSM) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please write this without using UsesSM, since that variable is updated further down the line. It seems better to explicitly for NewFD->hasAttr<ArmLocallyStreamingAttr>().

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// non-compitable streaming attribute. If it passed any VL-based arguments
// or return VL-based value, then warn that the streaming and non-streaming
// vector lengths may be different.
if (CallerFD && Context.getTargetInfo().hasFeature("sme") && !IsBuiltin) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

It is not.

}
if ((CallerFnType != ArmStreaming &&
CallerFnType != ArmStreamingCompatible && IsCalleeStreaming &&
AnyScalableArgsOrRet) ||
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you factor out AnyScalableArgsOrRet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

@sdesmalen-arm sdesmalen-arm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the update. I just left a few more suggestions to clean up the code a bit.

Comment on lines 3726 to 3727
"passing/returning a VL-dependent argument from a function"
" arm_locally_streaming attribute. The streaming and non-streaming vector"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"passing/returning a VL-dependent argument from a function"
" arm_locally_streaming attribute. The streaming and non-streaming vector"
"passing/returning a VL-dependent argument from a __arm_locally_streaming"
" function. The streaming and non-streaming vector"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -7513,6 +7516,30 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}

auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
bool IsCalleeStreaming =
(ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: parentheses are unnecessary.

Suggested change
(ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask);
ExtInfo.AArch64SMEAttributes & FunctionType::SME_PStateSMEnabledMask;

Also, please move these closer to their use (inside the if blocks)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7522 to 7524
bool IsCalleeStreamingCompatible =
(ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: parentheses are unnecessary.

Suggested change
bool IsCalleeStreamingCompatible =
(ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask);
bool IsCalleeStreamingCompatible =
ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

bool IsCalleeStreamingCompatible =
(ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask);
bool IsBuiltin = (FD && FD->getBuiltinID());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: parentheses are unnecessary.

Suggested change
bool IsBuiltin = (FD && FD->getBuiltinID());
bool IsBuiltin = FD && FD->getBuiltinID();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

(ExtInfo.AArch64SMEAttributes &
FunctionType::SME_PStateSMCompatibleMask);
bool IsBuiltin = (FD && FD->getBuiltinID());
AnyScalableArgsOrRet |= Proto->getReturnType()->isSizelessVectorType();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not initialise the variable with Proto->getReturnType()->isSizelessVectorType() and then remove this separate |= statement?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -7513,6 +7516,30 @@ void Sema::checkCall(NamedDecl *FDecl, const FunctionProtoType *Proto,
}
}

auto *CallerFD = dyn_cast<FunctionDecl>(CurContext);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: move this closer to its use, preferably wrap the condition, e.g.

  if (auto *CallerFD = dyn_cast<..>(..)) {
    ...
  }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7534 to 7539
if ((CallerFnType != ArmStreaming &&
CallerFnType != ArmStreamingCompatible && IsCalleeStreaming) ||
(CallerFnType == ArmStreaming && !IsCalleeStreaming &&
!IsCalleeStreamingCompatible) ||
(CallerFnType == ArmStreamingCompatible &&
(IsCalleeStreaming || !IsCalleeStreamingCompatible)))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified to:

if (!IsCalleeStreamingCompatible &&
    (CallerFnType == ArmStreamingCompatible ||
     ((CallerFnType == ArmStreaming) ^ IsCalleeStreaming)))

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment on lines 7528 to 7531
// If the caller is a function and the callee has a different
// non-compitable streaming attribute. If it passed any VL-based arguments
// or return VL-based value, then warn that the streaming and non-streaming
// vector lengths may be different.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment reads a bit strange. What about:

// If the call requires a streaming-mode change and has scalable vector
// arguments or return values, then warn the user that the streaming and
// non-streaming vector lengths may be different.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// If the call requires a streaming-mode change and has scalable vector
// arguments or return values, then warn the user that the streaming and
// non-streaming vector lengths may be different.
bool IsBuiltin = FD && FD->getBuiltinID();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

minor nit: this definition can be moved to inside the if (auto *CallerFD = ..) block.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

It's not though.

@@ -3723,8 +3723,8 @@ def warn_sme_streaming_pass_return_vl_to_non_streaming : Warning<
" different">,
InGroup<AArch64SMEAttributes>, DefaultIgnore;
def warn_sme_locally_streaming_has_vl_args_returns : Warning<
"passing/returning a VL-dependent argument from a function"
" arm_locally_streaming attribute. The streaming and non-streaming vector"
"passing/returning a VL-dependent argument from a arm_locally_streaming"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"passing/returning a VL-dependent argument from a arm_locally_streaming"
"passing/returning a VL-dependent argument from a __arm_locally_streaming"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

@@ -1390,6 +1390,9 @@ def MultiGPU: DiagGroup<"multi-gpu">;
// libc and the CRT to be skipped.
def AVRRtlibLinkingQuirks : DiagGroup<"avr-rtlib-linking-quirks">;

// A warning group AArch64 related to SME function attribues.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Sorry, just spotted this.

Suggested change
// A warning group AArch64 related to SME function attribues.
// A warning group related to AArch64 SME function attribues.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

" different">,
InGroup<AArch64SMEAttributes>, DefaultIgnore;
def warn_sme_locally_streaming_has_vl_args_returns : Warning<
"passing/returning a VL-dependent argument from a __arm_locally_streaming"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"passing/returning a VL-dependent argument from a __arm_locally_streaming"
"passing/returning a VL-dependent argument to/from a __arm_locally_streaming"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

// If the call requires a streaming-mode change and has scalable vector
// arguments or return values, then warn the user that the streaming and
// non-streaming vector lengths may be different.
bool IsBuiltin = FD && FD->getBuiltinID();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

It's not though.

Comment on lines +3720 to +3767
def warn_sme_streaming_pass_return_vl_to_non_streaming : Warning<
"passing a VL-dependent argument to/from a function that has a different"
" streaming-mode. The streaming and non-streaming vector lengths may be"
" different">,
InGroup<AArch64SMEAttributes>, DefaultIgnore;
def warn_sme_locally_streaming_has_vl_args_returns : Warning<
"passing/returning a VL-dependent argument to/from a __arm_locally_streaming"
" function. The streaming and non-streaming vector"
" lengths may be different">,
InGroup<AArch64SMEAttributes>, DefaultIgnore;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally I would prefer it if you could give these warnings a 'bool' operand, such that it would give a message about either the return value or the operand, for example:

def warn_sme_streaming_pass_return_vl_to_non_streaming : Warning<
  "%select{passing|returning}0 a VL-dependent argument %select{to|from}0 a function with a different"
  " streaming-mode is undefined behaviour if the streaming and non-streaming vector lengths are different at runtime">,
  InGroup<AArch64SMEAttributes>, DefaultIgnore;

And then emit the diagnostic like this:

if (ScalableArgs)
  Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming) << /*IsArg=*/true;
if (ScalableRet)
  Diag(Loc, diag::warn_sme_streaming_pass_return_vl_to_non_streaming) << /*IsArg=*/false;

This results in:

warning: returning a VL-dependent argument from a function with a different streaming-mode is undefined behaviour if the streaming and non-streaming vector lengths are different at runtime

and

warning: passing a VL-dependent argument to a function with a different streaming-mode is undefined behaviour if the streaming and non-streaming vector lengths are different at runtime

I won't hold up the patch for it, but I would appreciate it if you could do this either as part of this PR or as a follow-up patch.

Dinar Temirbulatov added 13 commits April 9, 2024 13:46
…or size might be different.

The compiler doesn't know in advance if the streaming and non-streaming
vector-lengths are different, so it should be safe to give a warning diagnostic
to warn the user about possible undefined behaviour. If the user knows
the vector lengths are equal, they can disable the warning separately.
streaming local function from streaming function.
@dtemirbulatov dtemirbulatov merged commit 4e85e1f into llvm:main Apr 10, 2024
@dtemirbulatov dtemirbulatov deleted the sme-warn-call branch April 10, 2024 07:49
@bgra8
Copy link
Contributor

bgra8 commented Apr 17, 2024

@dtemirbulatov this patch causes clang to crash. Please revert.

repro.cc:

typedef __SVFloat32_t a;
#pragma clang attribute push(__attribute__((target("+sve"))),                  \
                             apply_to = function)
template <int, int> struct b {
  using c = int;
};
template <class q> using e = q::c;
template <int f, int l> a g(b<f, l>, float);
template <class q> using h = decltype(g(q(), e<q>()));
h<b<0, 0>> i(b<0, 0>);
template <class q> using j = decltype(i(q()));
template <class k> struct m {
  static void n() {
    b<0, 0> d;
    k()(float(), d);
  }
};
template <class k> struct p {
  template <typename c, class q> void operator()(c, q p2) {
    int o;
    int misalignments[]{};
    for (int ma : misalignments)
      for (int mb : misalignments)
        k()(p2, 0, ma, mb, o);
  }
};
struct D {
  template <class q> void operator()(q, int, int, int, int) {
    [](j<q>) {};
  }
  void r() { m<p<D>>::n; }
};
#pragma clang attribute pop

Compilation command:

clang -cc1 -triple aarch64-unknown-linux-gnu  \
  -fsyntax-only \
  -std=gnu++20 \
  -x c++ \
  repro.cc

@dtemirbulatov
Copy link
Contributor Author

Reproduced the issue.

@bgra8
Copy link
Contributor

bgra8 commented Apr 17, 2024

@dtemirbulatov could you please revert to green to unblock us and continue the investigation asynchronously?

@dtemirbulatov
Copy link
Contributor Author

ok, Reverting now.

dtemirbulatov pushed a commit that referenced this pull request Apr 17, 2024
@dtemirbulatov
Copy link
Contributor Author

Reverted with 950bb09

dtemirbulatov pushed a commit that referenced this pull request Apr 19, 2024
aniplcc pushed a commit to aniplcc/llvm-project that referenced this pull request Apr 21, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants