diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 91112860a2d029..288e8232ca7444 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10751,9 +10751,9 @@ def err_multiversion_duplicate : Error< "multiversioned function redeclarations require identical target attributes">; def err_multiversion_noproto : Error< "multiversioned function must have a prototype">; -def err_multiversion_no_other_attrs : Error< +def err_multiversion_disallowed_other_attr : Error< "attribute '%select{target|cpu_specific|cpu_dispatch}0' multiversioning cannot be combined" - " with other attributes">; + " with attribute %1">; def err_multiversion_diff : Error< "multiversioned function declaration has a different %select{calling convention" "|return type|constexpr specification|inline specification|storage class|" diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 531c2801bf929f..ba05b0d32cf4c1 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -10037,23 +10037,37 @@ static bool AttrCompatibleWithMultiVersion(attr::Kind Kind, } } -static bool HasNonMultiVersionAttributes(const FunctionDecl *FD, - MultiVersionKind MVType) { +static bool checkNonMultiVersionCompatAttributes(Sema &S, + const FunctionDecl *FD, + const FunctionDecl *CausedFD, + MultiVersionKind MVType) { + bool IsCPUSpecificCPUDispatchMVType = + MVType == MultiVersionKind::CPUDispatch || + MVType == MultiVersionKind::CPUSpecific; + const auto Diagnose = [FD, CausedFD, IsCPUSpecificCPUDispatchMVType]( + Sema &S, const Attr *A) { + S.Diag(FD->getLocation(), diag::err_multiversion_disallowed_other_attr) + << IsCPUSpecificCPUDispatchMVType << A; + if (CausedFD) + S.Diag(CausedFD->getLocation(), diag::note_multiversioning_caused_here); + return true; + }; + for (const Attr *A : FD->attrs()) { switch (A->getKind()) { case attr::CPUDispatch: case attr::CPUSpecific: if (MVType != MultiVersionKind::CPUDispatch && MVType != MultiVersionKind::CPUSpecific) - return true; + return Diagnose(S, A); break; case attr::Target: if (MVType != MultiVersionKind::Target) - return true; + return Diagnose(S, A); break; default: if (!AttrCompatibleWithMultiVersion(A->getKind(), MVType)) - return true; + return Diagnose(S, A); break; } } @@ -10189,16 +10203,12 @@ static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, // For now, disallow all other attributes. These should be opt-in, but // an analysis of all of them is a future FIXME. - if (CausesMV && OldFD && HasNonMultiVersionAttributes(OldFD, MVType)) { - S.Diag(OldFD->getLocation(), diag::err_multiversion_no_other_attrs) - << IsCPUSpecificCPUDispatchMVType; - S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); + if (CausesMV && OldFD && + checkNonMultiVersionCompatAttributes(S, OldFD, NewFD, MVType)) return true; - } - if (HasNonMultiVersionAttributes(NewFD, MVType)) - return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs) - << IsCPUSpecificCPUDispatchMVType; + if (checkNonMultiVersionCompatAttributes(S, NewFD, nullptr, MVType)) + return true; // Only allow transition to MultiVersion if it hasn't been used. if (OldFD && CausesMV && OldFD->isUsed(false)) diff --git a/clang/test/Sema/attr-cpuspecific.c b/clang/test/Sema/attr-cpuspecific.c index ae86742ca0810b..e32c7a22894d6a 100644 --- a/clang/test/Sema/attr-cpuspecific.c +++ b/clang/test/Sema/attr-cpuspecific.c @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify %s +// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsyntax-only -verify %s -Wnonnull void __attribute__((cpu_specific(ivybridge))) no_default(void); void __attribute__((cpu_specific(sandybridge))) no_default(void); @@ -80,7 +80,7 @@ int __attribute((cpu_dispatch())) no_dispatch(void) {} // expected-error@+1 {{'cpu_specific' attribute takes at least 1 argument}} int __attribute((cpu_specific())) no_specific(void) {} -//expected-error@+1 {{attribute 'cpu_specific' multiversioning cannot be combined}} +//expected-error@+1 {{attribute 'cpu_specific' multiversioning cannot be combined with attribute 'used'}} void __attribute__((used,cpu_specific(sandybridge))) addtl_attrs(void); void __attribute__((target("default"))) addtl_attrs2(void); diff --git a/clang/test/Sema/attr-target-mv.c b/clang/test/Sema/attr-target-mv.c index e9156a6c73e737..33a2c4fa54ebf0 100644 --- a/clang/test/Sema/attr-target-mv.c +++ b/clang/test/Sema/attr-target-mv.c @@ -77,14 +77,14 @@ int prev_no_target2(void); int __attribute__((target("arch=ivybridge"))) prev_no_target2(void); void __attribute__((target("sse4.2"))) addtl_attrs(void); -//expected-error@+2 {{attribute 'target' multiversioning cannot be combined}} +//expected-error@+2 {{attribute 'target' multiversioning cannot be combined with attribute 'no_caller_saved_registers'}} void __attribute__((no_caller_saved_registers,target("arch=sandybridge"))) addtl_attrs(void); -//expected-error@+1 {{attribute 'target' multiversioning cannot be combined}} +//expected-error@+1 {{attribute 'target' multiversioning cannot be combined with attribute 'no_caller_saved_registers'}} void __attribute__((target("default"), no_caller_saved_registers)) addtl_attrs2(void); -//expected-error@+2 {{attribute 'target' multiversioning cannot be combined}} +//expected-error@+2 {{attribute 'target' multiversioning cannot be combined with attribute 'no_caller_saved_registers'}} //expected-note@+2 {{function multiversioning caused by this declaration}} void __attribute__((no_caller_saved_registers,target("sse4.2"))) addtl_attrs3(void); void __attribute__((target("arch=sandybridge"))) addtl_attrs3(void);