diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -42,6 +42,7 @@ class Constant; class ConstantInt; class Function; +class GlobalIFunc; class GlobalValue; class DataLayout; class FunctionType; @@ -352,6 +353,10 @@ /// we properly emit the iFunc. std::vector MultiVersionFuncs; + /// List of multiversion IFuncs we have emitted. Used to downgrade into + /// function declarations when we do not emit a definition for the resolver. + std::vector MultiVersionIFuncs; + typedef llvm::StringMap > ReplacementsTy; ReplacementsTy Replacements; @@ -1555,6 +1560,10 @@ void emitMultiVersionFunctions(); + /// Replace multiversion IFuncs whose resolver is undefined with function + /// declarations. + void replaceUndefinedMultiVersionIFuncs(); + /// Emit any vtables which we deferred and still have a use for. void EmitDeferredVTables(); diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -508,6 +508,7 @@ applyReplacements(); checkAliases(); emitMultiVersionFunctions(); + replaceUndefinedMultiVersionIFuncs(); EmitCXXGlobalInitFunc(); EmitCXXGlobalCleanUpFunc(); registerGlobalDtorsWithAtExit(); @@ -3289,6 +3290,22 @@ EmitGlobalFunctionDefinition(GD, GV); } +void CodeGenModule::replaceUndefinedMultiVersionIFuncs() { + for (llvm::GlobalIFunc *GIF : MultiVersionIFuncs) { + llvm::Function *Resolver = GIF->getResolverFunction(); + if (!Resolver->isDeclaration()) + continue; + + auto *DeclTy = cast(GIF->getValueType()); + auto *Decl = + llvm::Function::Create(DeclTy, llvm::Function::ExternalLinkage, + GIF->getAddressSpace(), "", &getModule()); + Decl->takeName(GIF); + GIF->replaceAllUsesWith(Decl); + GIF->eraseFromParent(); + } +} + void CodeGenModule::EmitGlobalDefinition(GlobalDecl GD, llvm::GlobalValue *GV) { const auto *D = cast(GD.getDecl()); @@ -3663,6 +3680,7 @@ "", Resolver, &getModule()); GIF->setName(ResolverName); SetCommonAttributes(FD, GIF); + MultiVersionIFuncs.push_back(GIF); return GIF; } diff --git a/clang/test/CodeGen/attr-cpuspecific.c b/clang/test/CodeGen/attr-cpuspecific.c --- a/clang/test/CodeGen/attr-cpuspecific.c +++ b/clang/test/CodeGen/attr-cpuspecific.c @@ -8,6 +8,7 @@ #endif // _MSC_VER // Each version should have an IFunc and an alias. +// LINUX: @SingleVersion = weak_odr alias void (), void ()* @SingleVersion.ifunc // LINUX: @TwoVersions = weak_odr alias void (), void ()* @TwoVersions.ifunc // LINUX: @TwoVersionsSameAttr = weak_odr alias void (), void ()* @TwoVersionsSameAttr.ifunc // LINUX: @ThreeVersionsSameAttr = weak_odr alias void (), void ()* @ThreeVersionsSameAttr.ifunc @@ -18,8 +19,8 @@ // LINUX: @GenericAndPentium = weak_odr alias i32 (i32, double), i32 (i32, double)* @GenericAndPentium.ifunc // LINUX: @DispatchFirst = weak_odr alias i32 (), i32 ()* @DispatchFirst.ifunc -// LINUX: @TwoVersions.ifunc = weak_odr ifunc void (), void ()* ()* @TwoVersions.resolver // LINUX: @SingleVersion.ifunc = weak_odr ifunc void (), void ()* ()* @SingleVersion.resolver +// LINUX: @TwoVersions.ifunc = weak_odr ifunc void (), void ()* ()* @TwoVersions.resolver // LINUX: @TwoVersionsSameAttr.ifunc = weak_odr ifunc void (), void ()* ()* @TwoVersionsSameAttr.resolver // LINUX: @ThreeVersionsSameAttr.ifunc = weak_odr ifunc void (), void ()* ()* @ThreeVersionsSameAttr.resolver // LINUX: @NoSpecifics.ifunc = weak_odr ifunc void (), void ()* ()* @NoSpecifics.resolver @@ -34,6 +35,21 @@ // LINUX: define{{.*}} void @SingleVersion.S() #[[S:[0-9]+]] // WINDOWS: define dso_local void @SingleVersion.S() #[[S:[0-9]+]] +ATTR(cpu_dispatch(ivybridge)) +void SingleVersion(void); +// LINUX: define weak_odr void ()* @SingleVersion.resolver() +// LINUX: call void @__cpu_indicator_init +// LINUX: ret void ()* @SingleVersion.S +// LINUX: call void @llvm.trap +// LINUX: unreachable + +// WINDOWS: define weak_odr dso_local void @SingleVersion() comdat +// WINDOWS: call void @__cpu_indicator_init() +// WINDOWS: call void @SingleVersion.S() +// WINDOWS-NEXT: ret void +// WINDOWS: call void @llvm.trap +// WINDOWS: unreachable + ATTR(cpu_specific(ivybridge)) void NotCalled(void){} // LINUX: define{{.*}} void @NotCalled.S() #[[S]] @@ -80,6 +96,10 @@ // CHECK: define {{.*}}void @ThreeVersionsSameAttr.S() #[[S]] // CHECK: define {{.*}}void @ThreeVersionsSameAttr.Z() #[[K]] +ATTR(cpu_specific(knl)) +int CpuSpecificNoDispatch(void) { return 1; } +// CHECK: define {{.*}}i32 @CpuSpecificNoDispatch.Z() #[[K:[0-9]+]] + void usages(void) { SingleVersion(); // LINUX: @SingleVersion.ifunc() @@ -93,6 +113,9 @@ ThreeVersionsSameAttr(); // LINUX: @ThreeVersionsSameAttr.ifunc() // WINDOWS: @ThreeVersionsSameAttr() + CpuSpecificNoDispatch(); + // LINUX: @CpuSpecificNoDispatch.ifunc() + // WINDOWS: @CpuSpecificNoDispatch() } // has an extra config to emit!