From b3fd2ea88875e58c66dd67870ff822cedf2570cf Mon Sep 17 00:00:00 2001 From: Alexandros Lamprineas Date: Mon, 2 Jun 2025 11:04:00 +0100 Subject: [Clang][FMV] Stop emitting implicit default version using target_clones. (#141808) With the current behavior the following example yields a linker error: "multiple definition of `foo.default'" // Translation Unit 1 __attribute__((target_clones("dotprod, sve"))) int foo(void) { return 1; } // Translation Unit 2 int foo(void) { return 0; } __attribute__((target_version("dotprod"))) int foo(void); __attribute__((target_version("sve"))) int foo(void); int bar(void) { return foo(); } That is because foo.default is generated twice. As a user I don't find this particularly intuitive. If I wanted the default to be generated in TU1 I'd rather write target_clones("dotprod, sve", "default") explicitly. When changing the code I noticed that the RISC-V target defers the resolver emission when encountering a target_version definition. This seems accidental since it only makes sense for AArch64, where we only emit a resolver once we've processed the entire TU, and only if the default version is present. I've changed this so that RISC-V immediately emmits the resolver. I adjusted the codegen tests since the functions now appear in a different order. Implements https://github.com/ARM-software/acle/pull/377 --- clang/lib/CodeGen/CodeGenModule.cpp | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'clang/lib/CodeGen/CodeGenModule.cpp') diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 6d2c705..9383c57 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -4237,19 +4237,19 @@ void CodeGenModule::EmitMultiVersionFunctionDefinition(GlobalDecl GD, EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr); } else if (auto *TC = FD->getAttr()) { for (unsigned I = 0; I < TC->featuresStrs_size(); ++I) - // AArch64 favors the default target version over the clone if any. - if ((!TC->isDefaultVersion(I) || !getTarget().getTriple().isAArch64()) && - TC->isFirstOfVersion(I)) + if (TC->isFirstOfVersion(I)) EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr); - // Ensure that the resolver function is also emitted. - GetOrCreateMultiVersionResolver(GD); } else EmitGlobalFunctionDefinition(GD, GV); - // Defer the resolver emission until we can reason whether the TU - // contains a default target version implementation. - if (FD->isTargetVersionMultiVersion()) - AddDeferredMultiVersionResolverToEmit(GD); + // Ensure that the resolver function is also emitted. + if (FD->isTargetVersionMultiVersion() || FD->isTargetClonesMultiVersion()) { + // On AArch64 defer the resolver emission until the entire TU is processed. + if (getTarget().getTriple().isAArch64()) + AddDeferredMultiVersionResolverToEmit(GD); + else + GetOrCreateMultiVersionResolver(GD); + } } void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { @@ -4351,7 +4351,7 @@ void CodeGenModule::emitMultiVersionFunctions() { }; // For AArch64, a resolver is only emitted if a function marked with - // target_version("default")) or target_clones() is present and defined + // target_version("default")) or target_clones("default") is defined // in this TU. For other architectures it is always emitted. bool ShouldEmitResolver = !getTarget().getTriple().isAArch64(); SmallVector Options; @@ -4374,12 +4374,11 @@ void CodeGenModule::emitMultiVersionFunctions() { TVA->getFeatures(Feats, Delim); Options.emplace_back(Func, Feats); } else if (const auto *TC = CurFD->getAttr()) { - if (IsDefined) - ShouldEmitResolver = true; for (unsigned I = 0; I < TC->featuresStrs_size(); ++I) { if (!TC->isFirstOfVersion(I)) continue; - + if (TC->isDefaultVersion(I) && IsDefined) + ShouldEmitResolver = true; llvm::Function *Func = createFunction(CurFD, I); Feats.clear(); if (getTarget().getTriple().isX86()) { -- cgit v1.1