[PATCH] D158666: [Clang] Fix linker error for function multiversioning

Elizabeth Andrews via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Wed Aug 23 13:13:29 PDT 2023


eandrews created this revision.
eandrews added reviewers: erichkeane, tahonermann, aaron.ballman, zsrkmyn.
Herald added a project: All.
eandrews requested review of this revision.

Currently target_clones attribute results in a linker error when there are no multi-versioned function declarations in the calling TU.

  foo.cpp
  int foo1();
  __attribute__((target_clones("default", "arch=core2")))
  int foo1() { return 0; }
  
  main.cpp
  int foo1();
  int main()
  {   return foo1(); }
  
  $ clang++ main.cpp foo.cpp
  /usr/bin/ld: /tmp/main-981c32.o: in function `main':
  main.cpp:(.text+0x10): undefined reference to `foo1()'
  clang++: error: linker command failed with exit code 1 (use -v to see invocation)

In the calling TU, the call is generated with ‘normal’ assembly name.  This does not match any of the versions since their mangling includes a .versionstring. The linker error is not seen with GCC since the mangling for the ifunc symbol in GCC is the ‘normal’ assembly name for function.

  Clang – 
  
  $nm foo.o
  U   __cpu_indicator_init
  U   __cpu_model
  T    _Z4foo1v.arch_core2.0
  T    _Z4foo1v.default.1
  i     _Z4foo1v.ifunc   <----------
  W  _Z4foo1v.resolver
  
  $ nm main.o
  T main
   U _Z4foo1v
  
  GCC
  
  $ nm foo.o
  U   __cpu_indicator_init
  U  __cpu_model
  i    _Z4foo1v <---------------
  t    _Z4foo1v.arch_core2.0
  t    _Z4foo1v.default.1
  W  _Z4foo1v.resolver
  
  $ nm main.o
  T main
  U _Z4foo1v

I was initially inclined to match GCC behavior here and remove the ifunc suffix but I decided against it because I am not sure why ifunc mangling was used in the first place. Maybe to maintain consistency between various multiversion attributes? I see target attribute also uses ifunc mangling (while GCC has some other mangling scheme for this attribute). As a less disruptive solution I just added an alias to the ifunc function, similar to what was done for CPU dispatch here - https://reviews.llvm.org/D67058. If the correct solution here is to remove the ifunc suffix, I can make that change instead.


https://reviews.llvm.org/D158666

Files:
  clang/lib/CodeGen/CodeGenModule.cpp
  clang/test/CodeGen/attr-target-clones.c
  clang/test/CodeGenCXX/attr-target-clones.cpp


Index: clang/test/CodeGenCXX/attr-target-clones.cpp
===================================================================
--- clang/test/CodeGenCXX/attr-target-clones.cpp
+++ clang/test/CodeGenCXX/attr-target-clones.cpp
@@ -1,6 +1,13 @@
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-linux-gnu -emit-llvm %s -o - | FileCheck %s --check-prefix=LINUX
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-pc -emit-llvm %s -o - | FileCheck %s --check-prefix=WINDOWS
 
+// Alias for ifunc
+// LINUX: @_Z10overloadedi = weak_odr alias i32 (i32), ptr @_Z10overloadedi.ifunc
+// LINUX: @_Z10overloadedPKc = weak_odr alias i32 (ptr), ptr @_Z10overloadedPKc.ifunc
+// LINUX: @_ZN1CIssE3fooEv = weak_odr alias i32 (ptr), ptr @_ZN1CIssE3fooEv.ifunc
+// LINUX: @_ZN1CIisE3fooEv = weak_odr alias i32 (ptr), ptr @_ZN1CIisE3fooEv.ifunc
+// LINUX: @_ZN1CIdfE3fooEv = weak_odr alias i32 (ptr), ptr @_ZN1CIdfE3fooEv.ifunc
+
 // Overloaded ifuncs
 // LINUX: @_Z10overloadedi.ifunc = weak_odr ifunc i32 (i32), ptr @_Z10overloadedi.resolver
 // LINUX: @_Z10overloadedPKc.ifunc = weak_odr ifunc i32 (ptr), ptr @_Z10overloadedPKc.resolver
Index: clang/test/CodeGen/attr-target-clones.c
===================================================================
--- clang/test/CodeGen/attr-target-clones.c
+++ clang/test/CodeGen/attr-target-clones.c
@@ -13,6 +13,13 @@
 // WINDOWS: $foo_inline = comdat any
 // WINDOWS: $foo_inline2 = comdat any
 
+// LINUX: @foo = weak_odr alias i32 (), ptr @foo.ifunc
+// LINUX: @foo_dupes = weak_odr alias void (), ptr @foo_dupes.ifunc
+// LINUX: @unused = weak_odr alias void (), ptr @unused.ifunc
+// LINUX: @foo_inline = weak_odr alias i32 (), ptr @foo_inline.ifunc
+// LINUX: @foo_inline2 = weak_odr alias i32 (), ptr @foo_inline2.ifunc
+// LINUX: @foo_used_no_defn = weak_odr alias i32 (), ptr @foo_used_no_defn.ifunc
+
 // LINUX: @foo.ifunc = weak_odr ifunc i32 (), ptr @foo.resolver
 // LINUX: @foo_dupes.ifunc = weak_odr ifunc void (), ptr @foo_dupes.resolver
 // LINUX: @unused.ifunc = weak_odr ifunc void (), ptr @unused.resolver
Index: clang/lib/CodeGen/CodeGenModule.cpp
===================================================================
--- clang/lib/CodeGen/CodeGenModule.cpp
+++ clang/lib/CodeGen/CodeGenModule.cpp
@@ -4053,8 +4053,20 @@
     }
 
     llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD);
-    if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant))
+    if (auto *IFunc = dyn_cast<llvm::GlobalIFunc>(ResolverConstant)) {
       ResolverConstant = IFunc->getResolver();
+      if (FD->isTargetClonesMultiVersion()) {
+        const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD);
+        llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI);
+        std::string MangledName = getMangledNameImpl(
+            *this, GD, FD, /*OmitMultiVersionMangling=*/true);
+        auto *Alias =
+            llvm::GlobalAlias::create(DeclTy, 0, llvm::Function::WeakODRLinkage,
+                                      MangledName, IFunc, &getModule());
+        SetCommonAttributes(FD, Alias);
+      }
+    }
+
     llvm::Function *ResolverFunc = cast<llvm::Function>(ResolverConstant);
 
     ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD));


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D158666.552862.patch
Type: text/x-patch
Size: 3228 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20230823/c1e13d9d/attachment.bin>


More information about the cfe-commits mailing list