Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenModule.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4280,7 +4280,7 @@ void CodeGenModule::emitMultiVersionFunctions() {
getContext().forEachMultiversionedFunctionVersion(
FD, [&](const FunctionDecl *CurFD) {
llvm::SmallVector<StringRef, 8> Feats;
bool IsDefined = CurFD->doesThisDeclarationHaveABody();
bool IsDefined = CurFD->getDefinition() != nullptr;

if (const auto *TA = CurFD->getAttr<TargetAttr>()) {
assert(getTarget().getTriple().isX86() && "Unsupported target");
Expand Down
47 changes: 22 additions & 25 deletions clang/lib/Sema/SemaDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11073,9 +11073,9 @@ bool Sema::shouldLinkDependentDeclWithPrevious(Decl *D, Decl *PrevDecl) {
static bool CheckMultiVersionValue(Sema &S, const FunctionDecl *FD) {
const auto *TA = FD->getAttr<TargetAttr>();
const auto *TVA = FD->getAttr<TargetVersionAttr>();
assert(
(TA || TVA) &&
"MultiVersion candidate requires a target or target_version attribute");

assert((TA || TVA) && "Expecting target or target_version attribute");

const TargetInfo &TargetInfo = S.Context.getTargetInfo();
enum ErrType { Feature = 0, Architecture = 1 };

Expand Down Expand Up @@ -11372,10 +11372,6 @@ static bool CheckMultiVersionFirstFunction(Sema &S, FunctionDecl *FD) {
// otherwise it is treated as a normal function.
if (TA && !TA->isDefaultVersion())
return false;
// The target_version attribute only causes Multiversioning if this
// declaration is NOT the default version.
if (TVA && TVA->isDefaultVersion())
return false;

if ((TA || TVA) && CheckMultiVersionValue(S, FD)) {
FD->setInvalidDecl();
Expand Down Expand Up @@ -11422,26 +11418,24 @@ static bool CheckDeclarationCausesMultiVersioning(Sema &S, FunctionDecl *OldFD,
LookupResult &Previous) {
assert(!OldFD->isMultiVersion() && "Unexpected MultiVersion");

const auto *NewTA = NewFD->getAttr<TargetAttr>();
const auto *OldTA = OldFD->getAttr<TargetAttr>();
const auto *NewTVA = NewFD->getAttr<TargetVersionAttr>();
const auto *OldTVA = OldFD->getAttr<TargetVersionAttr>();

assert((NewTA || NewTVA) && "Excpecting target or target_version attribute");

// The definitions should be allowed in any order. If we have discovered
// a new target version and the preceeding was the default, then add the
// corresponding attribute to it.
patchDefaultTargetVersion(NewFD, OldFD);

const auto *NewTA = NewFD->getAttr<TargetAttr>();
const auto *NewTVA = NewFD->getAttr<TargetVersionAttr>();
const auto *OldTA = OldFD->getAttr<TargetAttr>();

// If the old decl is NOT MultiVersioned yet, and we don't cause that
// to change, this is a simple redeclaration.
if (NewTA && !NewTA->isDefaultVersion() &&
(!OldTA || OldTA->getFeaturesStr() == NewTA->getFeaturesStr()))
return false;

// The target_version attribute only causes Multiversioning if this
// declaration is NOT the default version.
if (NewTVA && NewTVA->isDefaultVersion())
return false;

// Otherwise, this decl causes MultiVersioning.
if (CheckMultiVersionAdditionalRules(S, OldFD, NewFD, true,
NewTVA ? MultiVersionKind::TargetVersion
Expand All @@ -11456,15 +11450,16 @@ static bool CheckDeclarationCausesMultiVersioning(Sema &S, FunctionDecl *OldFD,
}

// If this is 'default', permit the forward declaration.
if (NewTA && NewTA->isDefaultVersion() && !OldTA) {
if ((NewTA && NewTA->isDefaultVersion() && !OldTA) ||
(NewTVA && NewTVA->isDefaultVersion() && !OldTVA)) {
Redeclaration = true;
OldDecl = OldFD;
OldFD->setIsMultiVersion();
NewFD->setIsMultiVersion();
return false;
}

if (CheckMultiVersionValue(S, OldFD)) {
if ((OldTA || OldTVA) && CheckMultiVersionValue(S, OldFD)) {
S.Diag(NewFD->getLocation(), diag::note_multiversioning_caused_here);
NewFD->setInvalidDecl();
return true;
Expand Down Expand Up @@ -11761,9 +11756,7 @@ static bool CheckMultiVersionAdditionalDecl(
// Else, this is simply a non-redecl case. Checking the 'value' is only
// necessary in the Target case, since The CPUSpecific/Dispatch cases are
// handled in the attribute adding step.
if ((NewMVKind == MultiVersionKind::TargetVersion ||
NewMVKind == MultiVersionKind::Target) &&
CheckMultiVersionValue(S, NewFD)) {
if ((NewTA || NewTVA) && CheckMultiVersionValue(S, NewFD)) {
NewFD->setInvalidDecl();
return true;
}
Expand Down Expand Up @@ -11799,6 +11792,12 @@ static bool CheckMultiVersionAdditionalDecl(
static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
bool &Redeclaration, NamedDecl *&OldDecl,
LookupResult &Previous) {
const TargetInfo &TI = S.getASTContext().getTargetInfo();

// Abort if FMV is disabled.
if (TI.getTriple().isAArch64() && !TI.hasFeature("fmv"))
return false;

const auto *NewTA = NewFD->getAttr<TargetAttr>();
const auto *NewTVA = NewFD->getAttr<TargetVersionAttr>();
const auto *NewCPUDisp = NewFD->getAttr<CPUDispatchAttr>();
Expand All @@ -11821,14 +11820,12 @@ static bool CheckMultiVersionFunction(Sema &S, FunctionDecl *NewFD,
return false;
}

const llvm::Triple &T = S.getASTContext().getTargetInfo().getTriple();

// Target attribute on AArch64 is not used for multiversioning
if (NewTA && T.isAArch64())
if (NewTA && TI.getTriple().isAArch64())
return false;

// Target attribute on RISCV is not used for multiversioning
if (NewTA && T.isRISCV())
if (NewTA && TI.getTriple().isRISCV())
return false;

if (!OldDecl || !OldDecl->getAsFunction() ||
Expand Down
221 changes: 221 additions & 0 deletions clang/test/CodeGen/AArch64/fmv-mix-explicit-implicit-default.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -emit-llvm -o - %s | FileCheck %s
// RUN: %clang_cc1 -triple aarch64-none-linux-gnu -target-feature -fmv -emit-llvm -o - %s | FileCheck %s -check-prefix=CHECK-NOFMV

int implicit_default_decl_first(void);
__attribute__((target_version("default"))) int implicit_default_decl_first(void) { return 1; }
int caller1(void) { return implicit_default_decl_first(); }

__attribute__((target_version("default"))) int explicit_default_def_first(void) { return 2; }
int explicit_default_def_first(void);
int caller2(void) { return explicit_default_def_first(); }

int implicit_default_def_first(void) { return 3; }
__attribute__((target_version("default"))) int implicit_default_def_first(void);
int caller3(void) { return implicit_default_def_first(); }

__attribute__((target_version("default"))) int explicit_default_decl_first(void);
int explicit_default_decl_first(void) { return 4; }
int caller4(void) { return explicit_default_decl_first(); }

int no_def_implicit_default_first(void);
__attribute__((target_version("default"))) int no_def_implicit_default_first(void);
int caller5(void) { return no_def_implicit_default_first(); }

__attribute__((target_version("default"))) int no_def_explicit_default_first(void);
int no_def_explicit_default_first(void);
int caller6(void) { return no_def_explicit_default_first(); }
//.
// CHECK: @implicit_default_decl_first = weak_odr ifunc i32 (), ptr @implicit_default_decl_first.resolver
// CHECK: @explicit_default_def_first = weak_odr ifunc i32 (), ptr @explicit_default_def_first.resolver
// CHECK: @implicit_default_def_first = weak_odr ifunc i32 (), ptr @implicit_default_def_first.resolver
// CHECK: @explicit_default_decl_first = weak_odr ifunc i32 (), ptr @explicit_default_decl_first.resolver
//.
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@implicit_default_decl_first.default
// CHECK-SAME: () #[[ATTR0:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 1
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller1
// CHECK-SAME: () #[[ATTR1:[0-9]+]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @implicit_default_decl_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@explicit_default_def_first.default
// CHECK-SAME: () #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 2
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller2
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @explicit_default_def_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@implicit_default_def_first.default
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 3
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller3
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @implicit_default_def_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@explicit_default_decl_first.default
// CHECK-SAME: () #[[ATTR0]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: ret i32 4
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller4
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @explicit_default_decl_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK: declare i32 @no_def_implicit_default_first() #[[ATTR2:[0-9]+]]
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller5
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @no_def_implicit_default_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK: declare i32 @no_def_explicit_default_first() #[[ATTR2]]
//
//
// CHECK: Function Attrs: noinline nounwind optnone
// CHECK-LABEL: define {{[^@]+}}@caller6
// CHECK-SAME: () #[[ATTR1]] {
// CHECK-NEXT: entry:
// CHECK-NEXT: [[CALL:%.*]] = call i32 @no_def_explicit_default_first()
// CHECK-NEXT: ret i32 [[CALL]]
//
//
// CHECK-LABEL: define {{[^@]+}}@implicit_default_decl_first.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @implicit_default_decl_first.default
//
//
// CHECK-LABEL: define {{[^@]+}}@explicit_default_def_first.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @explicit_default_def_first.default
//
//
// CHECK-LABEL: define {{[^@]+}}@implicit_default_def_first.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @implicit_default_def_first.default
//
//
// CHECK-LABEL: define {{[^@]+}}@explicit_default_decl_first.resolver() comdat {
// CHECK-NEXT: resolver_entry:
// CHECK-NEXT: ret ptr @explicit_default_decl_first.default
//
//
// CHECK: declare i32 @no_def_implicit_default_first.default() #[[ATTR2]]
//
//
// CHECK: declare i32 @no_def_explicit_default_first.default() #[[ATTR2]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller1
// CHECK-NOFMV-SAME: () #[[ATTR0:[0-9]+]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @implicit_default_decl_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@implicit_default_decl_first
// CHECK-NOFMV-SAME: () #[[ATTR1:[0-9]+]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: ret i32 1
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller2
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @explicit_default_def_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@explicit_default_def_first
// CHECK-NOFMV-SAME: () #[[ATTR1]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: ret i32 2
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@implicit_default_def_first
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: ret i32 3
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller3
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @implicit_default_def_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller4
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @explicit_default_decl_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@explicit_default_decl_first
// CHECK-NOFMV-SAME: () #[[ATTR1]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: ret i32 4
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller5
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @no_def_implicit_default_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: declare i32 @no_def_implicit_default_first() #[[ATTR2:[0-9]+]]
//
//
// CHECK-NOFMV: Function Attrs: noinline nounwind optnone
// CHECK-NOFMV-LABEL: define {{[^@]+}}@caller6
// CHECK-NOFMV-SAME: () #[[ATTR0]] {
// CHECK-NOFMV-NEXT: entry:
// CHECK-NOFMV-NEXT: [[CALL:%.*]] = call i32 @no_def_explicit_default_first()
// CHECK-NOFMV-NEXT: ret i32 [[CALL]]
//
//
// CHECK-NOFMV: declare i32 @no_def_explicit_default_first() #[[ATTR2]]
//.
Loading
Loading