diff options
| -rw-r--r-- | clang/lib/AST/ASTContext.cpp | 4 | ||||
| -rw-r--r-- | clang/lib/CodeGen/CodeGenModule.cpp | 6 | ||||
| -rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 52 | ||||
| -rw-r--r-- | clang/test/CodeGen/attr-target-mv.c | 53 | ||||
| -rw-r--r-- | clang/test/Sema/attr-target-mv.c | 3 |
5 files changed, 106 insertions, 12 deletions
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1ac46f897c5..464fa4bf4b5 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -9921,10 +9921,10 @@ void ASTContext::forEachMultiversionedFunctionVersion( llvm::function_ref<void(FunctionDecl *)> Pred) const { assert(FD->isMultiVersion() && "Only valid for multiversioned functions"); llvm::SmallDenseSet<const FunctionDecl*, 4> SeenDecls; - FD = FD->getCanonicalDecl(); + FD = FD->getMostRecentDecl(); for (auto *CurDecl : FD->getDeclContext()->getRedeclContext()->lookup(FD->getDeclName())) { - FunctionDecl *CurFD = CurDecl->getAsFunction()->getCanonicalDecl(); + FunctionDecl *CurFD = CurDecl->getAsFunction()->getMostRecentDecl(); if (CurFD && hasSameType(CurFD->getType(), FD->getType()) && std::end(SeenDecls) == llvm::find(SeenDecls, CurFD)) { SeenDecls.insert(CurFD); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index fa6cedd52f5..9ebd86a9819 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -1003,8 +1003,10 @@ void CodeGenModule::UpdateMultiVersionNames(GlobalDecl GD, "Other GD should now be a multiversioned function"); // OtherFD is the version of this function that was mangled BEFORE // becoming a MultiVersion function. It potentially needs to be updated. - const FunctionDecl *OtherFD = - OtherGD.getCanonicalDecl().getDecl()->getAsFunction(); + const FunctionDecl *OtherFD = OtherGD.getCanonicalDecl() + .getDecl() + ->getAsFunction() + ->getMostRecentDecl(); std::string OtherName = getMangledNameImpl(*this, OtherGD, OtherFD); // This is so that if the initial version was already the 'default' // version, we don't try to update it. diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index e34741608b2..8ccd9ae322b 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -9404,6 +9404,27 @@ static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) { return false; } +static bool HasNonMultiVersionAttributes(const FunctionDecl *FD, + MultiVersionKind MVType) { + for (const Attr *A : FD->attrs()) { + switch (A->getKind()) { + case attr::CPUDispatch: + case attr::CPUSpecific: + if (MVType != MultiVersionKind::CPUDispatch && + MVType != MultiVersionKind::CPUSpecific) + return true; + break; + case attr::Target: + if (MVType != MultiVersionKind::Target) + return true; + break; + default: + return true; + } + } + return false; +} + static bool CheckMultiVersionAdditionalRules(Sema &S, const FunctionDecl *OldFD, const FunctionDecl *NewFD, bool CausesMV, @@ -9449,15 +9470,14 @@ 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 && - std::distance(OldFD->attr_begin(), OldFD->attr_end()) != 1) { + 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); return true; } - if (std::distance(NewFD->attr_begin(), NewFD->attr_end()) != 1) + if (HasNonMultiVersionAttributes(NewFD, MVType)) return S.Diag(NewFD->getLocation(), diag::err_multiversion_no_other_attrs) << IsCPUSpecificCPUDispatchMVType; @@ -9581,6 +9601,15 @@ static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD, return false; } +static bool PreviousDeclsHaveMultiVersionAttribute(const FunctionDecl *FD) { + for (const Decl *D = FD->getPreviousDecl(); D; D = D->getPreviousDecl()) { + if (D->getAsFunction()->getMultiVersionKind() != MultiVersionKind::None) + return true; + } + + return false; +} + static bool CheckTargetCausesMultiVersioning( Sema &S, FunctionDecl *OldFD, FunctionDecl *NewFD, const TargetAttr *NewTA, bool &Redeclaration, NamedDecl *&OldDecl, bool &MergeTypeWithPrevious, @@ -9592,7 +9621,8 @@ static bool CheckTargetCausesMultiVersioning( // If the old decl is NOT MultiVersioned yet, and we don't cause that // to change, this is a simple redeclaration. - if (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr()) + if (!NewTA->isDefaultVersion() && + (!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr())) return false; // Otherwise, this decl causes MultiVersioning. @@ -9614,6 +9644,15 @@ static bool CheckTargetCausesMultiVersioning( return true; } + // If this is 'default', permit the forward declaration. + if (!OldFD->isMultiVersion() && !OldTA && NewTA->isDefaultVersion()) { + Redeclaration = true; + OldDecl = OldFD; + OldFD->setIsMultiVersion(); + NewFD->setIsMultiVersion(); + return false; + } + if (CheckMultiVersionValue(S, OldFD)) { S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); NewFD->setInvalidDecl(); @@ -9632,7 +9671,10 @@ static bool CheckTargetCausesMultiVersioning( for (const auto *FD : OldFD->redecls()) { const auto *CurTA = FD->getAttr<TargetAttr>(); - if (!CurTA || CurTA->isInherited()) { + // We allow forward declarations before ANY multiversioning attributes, but + // nothing after the fact. + if (PreviousDeclsHaveMultiVersionAttribute(FD) && + (!CurTA || CurTA->isInherited())) { S.Diag(FD->getLocation(), diag::err_multiversion_required_in_redecl) << 0; S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here); diff --git a/clang/test/CodeGen/attr-target-mv.c b/clang/test/CodeGen/attr-target-mv.c index f9fbbbd5f0b..e22499e2928 100644 --- a/clang/test/CodeGen/attr-target-mv.c +++ b/clang/test/CodeGen/attr-target-mv.c @@ -35,10 +35,24 @@ void bar4() { foo_multi(1, 5.0); } +int fwd_decl_default(void); +int __attribute__((target("default"))) fwd_decl_default(void) { return 2; } + +int fwd_decl_avx(void); +int __attribute__((target("avx"))) fwd_decl_avx(void) { return 2; } +int __attribute__((target("default"))) fwd_decl_avx(void) { return 2; } + +void bar5() { + fwd_decl_default(); + fwd_decl_avx(); +} + // LINUX: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver // LINUX: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.resolver // LINUX: @foo_decls.ifunc = ifunc void (), void ()* ()* @foo_decls.resolver // LINUX: @foo_multi.ifunc = ifunc void (i32, double), void (i32, double)* ()* @foo_multi.resolver +// LINUX: @fwd_decl_default.ifunc = ifunc i32 (), i32 ()* ()* @fwd_decl_default.resolver +// LINUX: @fwd_decl_avx.ifunc = ifunc i32 (), i32 ()* ()* @fwd_decl_avx.resolver // LINUX: define i32 @foo.sse4.2() // LINUX: ret i32 0 @@ -168,8 +182,45 @@ void bar4() { // WINDOWS: call void @foo_multi(i32 %0, double %1) // WINDOWS-NEXT: ret void -// LINUX: declare i32 @foo.arch_sandybridge() +// LINUX: define i32 @fwd_decl_default() +// LINUX: ret i32 2 +// LINUX: define i32 @fwd_decl_avx.avx() +// LINUX: ret i32 2 +// LINUX: define i32 @fwd_decl_avx() +// LINUX: ret i32 2 +// WINDOWS: define dso_local i32 @fwd_decl_default() +// WINDOWS: ret i32 2 +// WINDOWS: define dso_local i32 @fwd_decl_avx.avx() +// WINDOWS: ret i32 2 +// WINDOWS: define dso_local i32 @fwd_decl_avx() +// WINDOWS: ret i32 2 + +// LINUX: define void @bar5() +// LINUX: call i32 @fwd_decl_default.ifunc() +// LINUX: call i32 @fwd_decl_avx.ifunc() + +// WINDOWS: define dso_local void @bar5() +// WINDOWS: call i32 @fwd_decl_default.resolver() +// WINDOWS: call i32 @fwd_decl_avx.resolver() + +// LINUX: define i32 ()* @fwd_decl_default.resolver() comdat +// LINUX: call void @__cpu_indicator_init() +// LINUX: ret i32 ()* @fwd_decl_default +// LINUX: define i32 ()* @fwd_decl_avx.resolver() comdat +// LINUX: call void @__cpu_indicator_init() +// LINUX: ret i32 ()* @fwd_decl_avx.avx +// LINUX: ret i32 ()* @fwd_decl_avx + +// WINDOWS: define dso_local i32 @fwd_decl_default.resolver() comdat +// WINDOWS: call void @__cpu_indicator_init() +// WINDOWS: call i32 @fwd_decl_default +// WINDOWS: define dso_local i32 @fwd_decl_avx.resolver() comdat +// WINDOWS: call void @__cpu_indicator_init() +// WINDOWS: call i32 @fwd_decl_avx.avx +// WINDOWS: call i32 @fwd_decl_avx + +// LINUX: declare i32 @foo.arch_sandybridge() // WINDOWS: declare dso_local i32 @foo.arch_sandybridge() // LINUX: define linkonce i32 @foo_inline.sse4.2() diff --git a/clang/test/Sema/attr-target-mv.c b/clang/test/Sema/attr-target-mv.c index 671adff5b04..664ade1c0fa 100644 --- a/clang/test/Sema/attr-target-mv.c +++ b/clang/test/Sema/attr-target-mv.c @@ -65,10 +65,9 @@ int __attribute__((target("sse4.2,arch=sandybridge"))) mangle(void) { return 1; //expected-note@-2 {{previous declaration is here}} int __attribute__((target("arch=sandybridge,sse4.2"))) mangle(void) { return 2; } +// allow this, since we want to treat the 1st one as fwd-decl of the sandybridge version. int prev_no_target(void); int __attribute__((target("arch=sandybridge"))) prev_no_target(void) { return 2; } -// expected-error@-2 {{function declaration is missing 'target' attribute in a multiversioned function}} -// expected-note@+1 {{function multiversioning caused by this declaration}} int __attribute__((target("arch=ivybridge"))) prev_no_target(void) { return 2; } int __attribute__((target("arch=sandybridge"))) prev_no_target2(void); |

