Skip to content

[FMV][AArch64] Emit mangled default version if explicitly specified. #120022

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 4 commits into from
Dec 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
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 @@ -4283,7 +4283,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();

// Check 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