From: Fangrui Song Date: Wed, 11 Sep 2019 01:54:48 +0000 (+0000) Subject: [CodeGen] Add alias for cpu_dispatch function with IFunc & Fix resolver linkage type X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ab17a97bef93694218125ebb2d004d89dd7dd487;p=clang [CodeGen] Add alias for cpu_dispatch function with IFunc & Fix resolver linkage type Multi-versioned functions defined by cpu_dispatch and implemented with IFunc can not be called outside the translation units where they are defined due to lack of symbols. This patch add function aliases for these functions and thus make them visible outside. Differential Revision: https://reviews.llvm.org/D67058 Patch by Senran Zhang git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@371586 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CodeGenModule.cpp b/lib/CodeGen/CodeGenModule.cpp index eedf70b8d5..be7dfc97a9 100644 --- a/lib/CodeGen/CodeGenModule.cpp +++ b/lib/CodeGen/CodeGenModule.cpp @@ -2836,11 +2836,13 @@ void CodeGenModule::emitMultiVersionFunctions() { llvm::Function *ResolverFunc; const TargetInfo &TI = getTarget(); - if (TI.supportsIFunc() || FD->isTargetMultiVersion()) + if (TI.supportsIFunc() || FD->isTargetMultiVersion()) { ResolverFunc = cast( GetGlobalValue((getMangledName(GD) + ".resolver").str())); - else + ResolverFunc->setLinkage(llvm::Function::WeakODRLinkage); + } else { ResolverFunc = cast(GetGlobalValue(getMangledName(GD))); + } if (supportsCOMDAT()) ResolverFunc->setComdat( @@ -2884,6 +2886,10 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { auto *ResolverFunc = cast(GetOrCreateLLVMFunction( ResolverName, ResolverType, ResolverGD, /*ForVTable=*/false)); + ResolverFunc->setLinkage(llvm::Function::WeakODRLinkage); + if (supportsCOMDAT()) + ResolverFunc->setComdat( + getModule().getOrInsertComdat(ResolverFunc->getName())); SmallVector Options; const TargetInfo &Target = getTarget(); @@ -2948,6 +2954,21 @@ void CodeGenModule::emitCPUDispatchDefinition(GlobalDecl GD) { CodeGenFunction CGF(*this); CGF.EmitMultiVersionResolver(ResolverFunc, Options); + + if (getTarget().supportsIFunc()) { + std::string AliasName = getMangledNameImpl( + *this, GD, FD, /*OmitMultiVersionMangling=*/true); + llvm::Constant *AliasFunc = GetGlobalValue(AliasName); + if (!AliasFunc) { + auto *IFunc = cast(GetOrCreateLLVMFunction( + AliasName, DeclTy, GD, /*ForVTable=*/false, /*DontDefer=*/true, + /*IsThunk=*/false, llvm::AttributeList(), NotForDefinition)); + auto *GA = llvm::GlobalAlias::create( + DeclTy, 0, getFunctionLinkage(GD), AliasName, IFunc, &getModule()); + GA->setLinkage(llvm::Function::WeakODRLinkage); + SetCommonAttributes(GD, GA); + } + } } /// If a dispatcher for the specified mangled name is not in the module, create @@ -2984,7 +3005,7 @@ llvm::Constant *CodeGenModule::GetOrCreateMultiVersionResolver( MangledName + ".resolver", ResolverType, GlobalDecl{}, /*ForVTable=*/false); llvm::GlobalIFunc *GIF = llvm::GlobalIFunc::create( - DeclTy, 0, llvm::Function::ExternalLinkage, "", Resolver, &getModule()); + DeclTy, 0, llvm::Function::WeakODRLinkage, "", Resolver, &getModule()); GIF->setName(ResolverName); SetCommonAttributes(FD, GIF); diff --git a/test/CodeGen/attr-cpuspecific.c b/test/CodeGen/attr-cpuspecific.c index 744af1454a..e249c87dfb 100644 --- a/test/CodeGen/attr-cpuspecific.c +++ b/test/CodeGen/attr-cpuspecific.c @@ -7,11 +7,27 @@ #define ATTR(X) __attribute__((X)) #endif // _MSC_VER -// Each called version should have an IFunc. -// LINUX: @SingleVersion.ifunc = ifunc void (), void ()* ()* @SingleVersion.resolver -// LINUX: @TwoVersions.ifunc = ifunc void (), void ()* ()* @TwoVersions.resolver -// LINUX: @TwoVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @TwoVersionsSameAttr.resolver -// LINUX: @ThreeVersionsSameAttr.ifunc = ifunc void (), void ()* ()* @ThreeVersionsSameAttr.resolver +// Each version should have an IFunc and an alias. +// 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 +// LINUX: @NoSpecifics = weak_odr alias void (), void ()* @NoSpecifics.ifunc +// LINUX: @HasGeneric = weak_odr alias void (), void ()* @HasGeneric.ifunc +// LINUX: @HasParams = weak_odr alias void (i32, double), void (i32, double)* @HasParams.ifunc +// LINUX: @HasParamsAndReturn = weak_odr alias i32 (i32, double), i32 (i32, double)* @HasParamsAndReturn.ifunc +// 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: @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 +// LINUX: @HasGeneric.ifunc = weak_odr ifunc void (), void ()* ()* @HasGeneric.resolver +// LINUX: @HasParams.ifunc = weak_odr ifunc void (i32, double), void (i32, double)* ()* @HasParams.resolver +// LINUX: @HasParamsAndReturn.ifunc = weak_odr ifunc i32 (i32, double), i32 (i32, double)* ()* @HasParamsAndReturn.resolver +// LINUX: @GenericAndPentium.ifunc = weak_odr ifunc i32 (i32, double), i32 (i32, double)* ()* @GenericAndPentium.resolver +// LINUX: @DispatchFirst.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @DispatchFirst.resolver ATTR(cpu_specific(ivybridge)) void SingleVersion(void){} @@ -29,14 +45,14 @@ void TwoVersions(void); ATTR(cpu_dispatch(ivybridge, knl)) void TwoVersions(void); -// LINUX: define void ()* @TwoVersions.resolver() +// LINUX: define weak_odr void ()* @TwoVersions.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret void ()* @TwoVersions.Z // LINUX: ret void ()* @TwoVersions.S // LINUX: call void @llvm.trap // LINUX: unreachable -// WINDOWS: define dso_local void @TwoVersions() +// WINDOWS: define weak_odr dso_local void @TwoVersions() comdat // WINDOWS: call void @__cpu_indicator_init() // WINDOWS: call void @TwoVersions.Z() // WINDOWS-NEXT: ret void @@ -82,14 +98,14 @@ void usages() { // has an extra config to emit! ATTR(cpu_dispatch(ivybridge, knl, atom)) void TwoVersionsSameAttr(void); -// LINUX: define void ()* @TwoVersionsSameAttr.resolver() +// LINUX: define weak_odr void ()* @TwoVersionsSameAttr.resolver() // LINUX: ret void ()* @TwoVersionsSameAttr.Z // LINUX: ret void ()* @TwoVersionsSameAttr.S // LINUX: ret void ()* @TwoVersionsSameAttr.O // LINUX: call void @llvm.trap // LINUX: unreachable -// WINDOWS: define dso_local void @TwoVersionsSameAttr() +// WINDOWS: define weak_odr dso_local void @TwoVersionsSameAttr() comdat // WINDOWS: call void @TwoVersionsSameAttr.Z // WINDOWS-NEXT: ret void // WINDOWS: call void @TwoVersionsSameAttr.S @@ -101,7 +117,7 @@ void TwoVersionsSameAttr(void); ATTR(cpu_dispatch(atom, ivybridge, knl)) void ThreeVersionsSameAttr(void){} -// LINUX: define void ()* @ThreeVersionsSameAttr.resolver() +// LINUX: define weak_odr void ()* @ThreeVersionsSameAttr.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret void ()* @ThreeVersionsSameAttr.Z // LINUX: ret void ()* @ThreeVersionsSameAttr.S @@ -109,7 +125,7 @@ void ThreeVersionsSameAttr(void){} // LINUX: call void @llvm.trap // LINUX: unreachable -// WINDOWS: define dso_local void @ThreeVersionsSameAttr() +// WINDOWS: define weak_odr dso_local void @ThreeVersionsSameAttr() comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: call void @ThreeVersionsSameAttr.Z // WINDOWS-NEXT: ret void @@ -123,7 +139,7 @@ void ThreeVersionsSameAttr(void){} // No Cpu Specific options. ATTR(cpu_dispatch(atom, ivybridge, knl)) void NoSpecifics(void); -// LINUX: define void ()* @NoSpecifics.resolver() +// LINUX: define weak_odr void ()* @NoSpecifics.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret void ()* @NoSpecifics.Z // LINUX: ret void ()* @NoSpecifics.S @@ -131,7 +147,7 @@ void NoSpecifics(void); // LINUX: call void @llvm.trap // LINUX: unreachable -// WINDOWS: define dso_local void @NoSpecifics() +// WINDOWS: define weak_odr dso_local void @NoSpecifics() comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: call void @NoSpecifics.Z // WINDOWS-NEXT: ret void @@ -144,7 +160,7 @@ void NoSpecifics(void); ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) void HasGeneric(void); -// LINUX: define void ()* @HasGeneric.resolver() +// LINUX: define weak_odr void ()* @HasGeneric.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret void ()* @HasGeneric.Z // LINUX: ret void ()* @HasGeneric.S @@ -152,7 +168,7 @@ void HasGeneric(void); // LINUX: ret void ()* @HasGeneric.A // LINUX-NOT: call void @llvm.trap -// WINDOWS: define dso_local void @HasGeneric() +// WINDOWS: define weak_odr dso_local void @HasGeneric() comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: call void @HasGeneric.Z // WINDOWS-NEXT: ret void @@ -166,7 +182,7 @@ void HasGeneric(void); ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) void HasParams(int i, double d); -// LINUX: define void (i32, double)* @HasParams.resolver() +// LINUX: define weak_odr void (i32, double)* @HasParams.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret void (i32, double)* @HasParams.Z // LINUX: ret void (i32, double)* @HasParams.S @@ -174,7 +190,7 @@ void HasParams(int i, double d); // LINUX: ret void (i32, double)* @HasParams.A // LINUX-NOT: call void @llvm.trap -// WINDOWS: define dso_local void @HasParams(i32 %0, double %1) +// WINDOWS: define weak_odr dso_local void @HasParams(i32 %0, double %1) comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: call void @HasParams.Z(i32 %0, double %1) // WINDOWS-NEXT: ret void @@ -188,7 +204,7 @@ void HasParams(int i, double d); ATTR(cpu_dispatch(atom, generic, ivybridge, knl)) int HasParamsAndReturn(int i, double d); -// LINUX: define i32 (i32, double)* @HasParamsAndReturn.resolver() +// LINUX: define weak_odr i32 (i32, double)* @HasParamsAndReturn.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.Z // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.S @@ -196,7 +212,7 @@ int HasParamsAndReturn(int i, double d); // LINUX: ret i32 (i32, double)* @HasParamsAndReturn.A // LINUX-NOT: call void @llvm.trap -// WINDOWS: define dso_local i32 @HasParamsAndReturn(i32 %0, double %1) +// WINDOWS: define weak_odr dso_local i32 @HasParamsAndReturn(i32 %0, double %1) comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: %[[RET:.+]] = musttail call i32 @HasParamsAndReturn.Z(i32 %0, double %1) // WINDOWS-NEXT: ret i32 %[[RET]] @@ -210,14 +226,14 @@ int HasParamsAndReturn(int i, double d); ATTR(cpu_dispatch(atom, generic, pentium)) int GenericAndPentium(int i, double d); -// LINUX: define i32 (i32, double)* @GenericAndPentium.resolver() +// LINUX: define weak_odr i32 (i32, double)* @GenericAndPentium.resolver() // LINUX: call void @__cpu_indicator_init // LINUX: ret i32 (i32, double)* @GenericAndPentium.O // LINUX: ret i32 (i32, double)* @GenericAndPentium.B // LINUX-NOT: ret i32 (i32, double)* @GenericAndPentium.A // LINUX-NOT: call void @llvm.trap -// WINDOWS: define dso_local i32 @GenericAndPentium(i32 %0, double %1) +// WINDOWS: define weak_odr dso_local i32 @GenericAndPentium(i32 %0, double %1) comdat // WINDOWS: call void @__cpu_indicator_init // WINDOWS: %[[RET:.+]] = musttail call i32 @GenericAndPentium.O(i32 %0, double %1) // WINDOWS-NEXT: ret i32 %[[RET]] @@ -228,11 +244,11 @@ int GenericAndPentium(int i, double d); ATTR(cpu_dispatch(atom, pentium)) int DispatchFirst(void); -// LINUX: define i32 ()* @DispatchFirst.resolver +// LINUX: define weak_odr i32 ()* @DispatchFirst.resolver // LINUX: ret i32 ()* @DispatchFirst.O // LINUX: ret i32 ()* @DispatchFirst.B -// WINDOWS: define dso_local i32 @DispatchFirst() +// WINDOWS: define weak_odr dso_local i32 @DispatchFirst() comdat // WINDOWS: %[[RET:.+]] = musttail call i32 @DispatchFirst.O() // WINDOWS-NEXT: ret i32 %[[RET]] // WINDOWS: %[[RET:.+]] = musttail call i32 @DispatchFirst.B() diff --git a/test/CodeGen/attr-target-mv-func-ptrs.c b/test/CodeGen/attr-target-mv-func-ptrs.c index d1ff80050c..4361a7aeb9 100644 --- a/test/CodeGen/attr-target-mv-func-ptrs.c +++ b/test/CodeGen/attr-target-mv-func-ptrs.c @@ -17,7 +17,7 @@ int bar() { return Free(1) + Free(2); } -// LINUX: @foo.ifunc = ifunc i32 (i32), i32 (i32)* ()* @foo.resolver +// LINUX: @foo.ifunc = weak_odr ifunc i32 (i32), i32 (i32)* ()* @foo.resolver // LINUX: define i32 @foo.sse4.2( // LINUX: ret i32 0 // LINUX: define i32 @foo.arch_ivybridge( diff --git a/test/CodeGen/attr-target-mv-va-args.c b/test/CodeGen/attr-target-mv-va-args.c index ac13e6452d..9d51d090b1 100644 --- a/test/CodeGen/attr-target-mv-va-args.c +++ b/test/CodeGen/attr-target-mv-va-args.c @@ -9,7 +9,7 @@ int bar() { return foo(1, 'a', 1.1) + foo(2, 2.2, "asdf"); } -// LINUX: @foo.ifunc = ifunc i32 (i32, ...), i32 (i32, ...)* ()* @foo.resolver +// LINUX: @foo.ifunc = weak_odr ifunc i32 (i32, ...), i32 (i32, ...)* ()* @foo.resolver // LINUX: define i32 @foo.sse4.2(i32 %i, ...) // LINUX: ret i32 0 // LINUX: define i32 @foo.arch_ivybridge(i32 %i, ...) @@ -20,7 +20,7 @@ int bar() { // LINUX: call i32 (i32, ...) @foo.ifunc(i32 1, i32 97, double // LINUX: call i32 (i32, ...) @foo.ifunc(i32 2, double 2.2{{[0-9Ee+]+}}, i8* getelementptr inbounds -// LINUX: define i32 (i32, ...)* @foo.resolver() comdat +// LINUX: define weak_odr i32 (i32, ...)* @foo.resolver() comdat // LINUX: ret i32 (i32, ...)* @foo.arch_sandybridge // LINUX: ret i32 (i32, ...)* @foo.arch_ivybridge // LINUX: ret i32 (i32, ...)* @foo.sse4.2 @@ -37,7 +37,7 @@ int bar() { // WINDOWS: call i32 (i32, ...) @foo.resolver(i32 1, i32 97, double // WINDOWS: call i32 (i32, ...) @foo.resolver(i32 2, double 2.2{{[0-9Ee+]+}}, i8* getelementptr inbounds -// WINDOWS: define dso_local i32 @foo.resolver(i32 %0, ...) comdat +// WINDOWS: define weak_odr dso_local i32 @foo.resolver(i32 %0, ...) comdat // WINDOWS: musttail call i32 (i32, ...) @foo.arch_sandybridge // WINDOWS: musttail call i32 (i32, ...) @foo.arch_ivybridge // WINDOWS: musttail call i32 (i32, ...) @foo.sse4.2 diff --git a/test/CodeGen/attr-target-mv.c b/test/CodeGen/attr-target-mv.c index 115432f011..c0c3de5f99 100644 --- a/test/CodeGen/attr-target-mv.c +++ b/test/CodeGen/attr-target-mv.c @@ -47,12 +47,12 @@ void bar5() { fwd_decl_avx(); } -// LINUX: @foo.ifunc = ifunc i32 (), i32 ()* ()* @foo.resolver -// LINUX: @foo_inline.ifunc = ifunc i32 (), i32 ()* ()* @foo_inline.resolver -// LINUX: @foo_decls.ifunc = ifunc void (), void ()* ()* @foo_decls.resolver -// LINUX: @foo_multi.ifunc = ifunc void (i32, double), void (i32, double)* ()* @foo_multi.resolver -// LINUX: @fwd_decl_default.ifunc = ifunc i32 (), i32 ()* ()* @fwd_decl_default.resolver -// LINUX: @fwd_decl_avx.ifunc = ifunc i32 (), i32 ()* ()* @fwd_decl_avx.resolver +// LINUX: @foo.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo.resolver +// LINUX: @foo_inline.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo_inline.resolver +// LINUX: @foo_decls.ifunc = weak_odr ifunc void (), void ()* ()* @foo_decls.resolver +// LINUX: @foo_multi.ifunc = weak_odr ifunc void (i32, double), void (i32, double)* ()* @foo_multi.resolver +// LINUX: @fwd_decl_default.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @fwd_decl_default.resolver +// LINUX: @fwd_decl_avx.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @fwd_decl_avx.resolver // LINUX: define i32 @foo.sse4.2() // LINUX: ret i32 0 @@ -72,14 +72,14 @@ void bar5() { // WINDOWS: define dso_local i32 @bar() // WINDOWS: call i32 @foo.resolver() -// LINUX: define i32 ()* @foo.resolver() comdat +// LINUX: define weak_odr i32 ()* @foo.resolver() comdat // LINUX: call void @__cpu_indicator_init() // LINUX: ret i32 ()* @foo.arch_sandybridge // LINUX: ret i32 ()* @foo.arch_ivybridge // LINUX: ret i32 ()* @foo.sse4.2 // LINUX: ret i32 ()* @foo -// WINDOWS: define dso_local i32 @foo.resolver() comdat +// WINDOWS: define weak_odr dso_local i32 @foo.resolver() comdat // WINDOWS: call void @__cpu_indicator_init() // WINDOWS: call i32 @foo.arch_sandybridge // WINDOWS: call i32 @foo.arch_ivybridge @@ -92,14 +92,14 @@ void bar5() { // WINDOWS: define dso_local i32 @bar2() // WINDOWS: call i32 @foo_inline.resolver() -// LINUX: define i32 ()* @foo_inline.resolver() comdat +// LINUX: define weak_odr i32 ()* @foo_inline.resolver() comdat // LINUX: call void @__cpu_indicator_init() // LINUX: ret i32 ()* @foo_inline.arch_sandybridge // LINUX: ret i32 ()* @foo_inline.arch_ivybridge // LINUX: ret i32 ()* @foo_inline.sse4.2 // LINUX: ret i32 ()* @foo_inline -// WINDOWS: define dso_local i32 @foo_inline.resolver() comdat +// WINDOWS: define weak_odr dso_local i32 @foo_inline.resolver() comdat // WINDOWS: call void @__cpu_indicator_init() // WINDOWS: call i32 @foo_inline.arch_sandybridge // WINDOWS: call i32 @foo_inline.arch_ivybridge @@ -112,11 +112,11 @@ void bar5() { // WINDOWS: define dso_local void @bar3() // WINDOWS: call void @foo_decls.resolver() -// LINUX: define void ()* @foo_decls.resolver() comdat +// LINUX: define weak_odr void ()* @foo_decls.resolver() comdat // LINUX: ret void ()* @foo_decls.sse4.2 // LINUX: ret void ()* @foo_decls -// WINDOWS: define dso_local void @foo_decls.resolver() comdat +// WINDOWS: define weak_odr dso_local void @foo_decls.resolver() comdat // WINDOWS: call void @foo_decls.sse4.2 // WINDOWS: call void @foo_decls @@ -126,7 +126,7 @@ void bar5() { // WINDOWS: define dso_local void @bar4() // WINDOWS: call void @foo_multi.resolver(i32 1, double 5.{{[0+e]*}}) -// LINUX: define void (i32, double)* @foo_multi.resolver() comdat +// LINUX: define weak_odr void (i32, double)* @foo_multi.resolver() comdat // LINUX: and i32 %{{.*}}, 4352 // LINUX: icmp eq i32 %{{.*}}, 4352 // LINUX: ret void (i32, double)* @foo_multi.fma4_sse4.2 @@ -139,7 +139,7 @@ void bar5() { // LINUX: ret void (i32, double)* @foo_multi.avx_sse4.2 // LINUX: ret void (i32, double)* @foo_multi -// WINDOWS: define dso_local void @foo_multi.resolver(i32 %0, double %1) comdat +// WINDOWS: define weak_odr dso_local void @foo_multi.resolver(i32 %0, double %1) comdat // WINDOWS: and i32 %{{.*}}, 4352 // WINDOWS: icmp eq i32 %{{.*}}, 4352 // WINDOWS: call void @foo_multi.fma4_sse4.2(i32 %0, double %1) @@ -178,18 +178,18 @@ void bar5() { // WINDOWS: call i32 @fwd_decl_default.resolver() // WINDOWS: call i32 @fwd_decl_avx.resolver() -// LINUX: define i32 ()* @fwd_decl_default.resolver() comdat +// LINUX: define weak_odr i32 ()* @fwd_decl_default.resolver() comdat // LINUX: call void @__cpu_indicator_init() // LINUX: ret i32 ()* @fwd_decl_default -// LINUX: define i32 ()* @fwd_decl_avx.resolver() comdat +// LINUX: define weak_odr i32 ()* @fwd_decl_avx.resolver() comdat // LINUX: call void @__cpu_indicator_init() // LINUX: ret i32 ()* @fwd_decl_avx.avx // LINUX: ret i32 ()* @fwd_decl_avx -// WINDOWS: define dso_local i32 @fwd_decl_default.resolver() comdat +// WINDOWS: define weak_odr dso_local i32 @fwd_decl_default.resolver() comdat // WINDOWS: call void @__cpu_indicator_init() // WINDOWS: call i32 @fwd_decl_default -// WINDOWS: define dso_local i32 @fwd_decl_avx.resolver() comdat +// WINDOWS: define weak_odr dso_local i32 @fwd_decl_avx.resolver() comdat // WINDOWS: call void @__cpu_indicator_init() // WINDOWS: call i32 @fwd_decl_avx.avx // WINDOWS: call i32 @fwd_decl_avx diff --git a/test/CodeGenCXX/attr-cpuspecific.cpp b/test/CodeGenCXX/attr-cpuspecific.cpp index 7601859df2..efe6921ec1 100644 --- a/test/CodeGenCXX/attr-cpuspecific.cpp +++ b/test/CodeGenCXX/attr-cpuspecific.cpp @@ -13,13 +13,15 @@ void foo() { s.Func(); } -// LINUX: define void (%struct.S*)* @_ZN1S4FuncEv.resolver +// LINUX: @_ZN1S4FuncEv = weak_odr alias void (%struct.S*), void (%struct.S*)* @_ZN1S4FuncEv.ifunc +// LINUX: @_ZN1S4FuncEv.ifunc = weak_odr ifunc void (%struct.S*), void (%struct.S*)* ()* @_ZN1S4FuncEv.resolver +// LINUX: define weak_odr void (%struct.S*)* @_ZN1S4FuncEv.resolver // LINUX: ret void (%struct.S*)* @_ZN1S4FuncEv.S // LINUX: ret void (%struct.S*)* @_ZN1S4FuncEv.O // LINUX: declare void @_ZN1S4FuncEv.S // LINUX: define linkonce_odr void @_ZN1S4FuncEv.O -// WINDOWS: define dso_local void @"?Func@S@@QEAAXXZ"(%struct.S* %0) +// WINDOWS: define weak_odr dso_local void @"?Func@S@@QEAAXXZ"(%struct.S* %0) comdat // WINDOWS: musttail call void @"?Func@S@@QEAAXXZ.S"(%struct.S* %0) // WINDOWS: musttail call void @"?Func@S@@QEAAXXZ.O"(%struct.S* %0) // WINDOWS: declare dso_local void @"?Func@S@@QEAAXXZ.S" diff --git a/test/CodeGenCXX/attr-target-mv-diff-ns.cpp b/test/CodeGenCXX/attr-target-mv-diff-ns.cpp index 4d4841d49c..3c47af9bb9 100644 --- a/test/CodeGenCXX/attr-target-mv-diff-ns.cpp +++ b/test/CodeGenCXX/attr-target-mv-diff-ns.cpp @@ -18,8 +18,8 @@ int bar() { return foo(1) + ns::foo(2); } -// LINUX: @_Z3fooi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z3fooi.resolver -// LINUX: @_ZN2ns3fooEi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_ZN2ns3fooEi.resolver +// LINUX: @_Z3fooi.ifunc = weak_odr ifunc i32 (i32), i32 (i32)* ()* @_Z3fooi.resolver +// LINUX: @_ZN2ns3fooEi.ifunc = weak_odr ifunc i32 (i32), i32 (i32)* ()* @_ZN2ns3fooEi.resolver // LINUX: define i32 @_Z3fooi.sse4.2(i32 %0) // LINUX: ret i32 0 @@ -57,25 +57,25 @@ int bar() { // WINDOWS: call i32 @"?foo@@YAHH@Z.resolver"(i32 1) // WINDOWS: call i32 @"?foo@ns@@YAHH@Z.resolver"(i32 2) -// LINUX: define i32 (i32)* @_Z3fooi.resolver() comdat +// LINUX: define weak_odr i32 (i32)* @_Z3fooi.resolver() comdat // LINUX: ret i32 (i32)* @_Z3fooi.arch_sandybridge // LINUX: ret i32 (i32)* @_Z3fooi.arch_ivybridge // LINUX: ret i32 (i32)* @_Z3fooi.sse4.2 // LINUX: ret i32 (i32)* @_Z3fooi -// WINDOWS: define dso_local i32 @"?foo@@YAHH@Z.resolver"(i32 %0) comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo@@YAHH@Z.resolver"(i32 %0) comdat // WINDOWS: call i32 @"?foo@@YAHH@Z.arch_sandybridge"(i32 %0) // WINDOWS: call i32 @"?foo@@YAHH@Z.arch_ivybridge"(i32 %0) // WINDOWS: call i32 @"?foo@@YAHH@Z.sse4.2"(i32 %0) // WINDOWS: call i32 @"?foo@@YAHH@Z"(i32 %0) -// LINUX: define i32 (i32)* @_ZN2ns3fooEi.resolver() comdat +// LINUX: define weak_odr i32 (i32)* @_ZN2ns3fooEi.resolver() comdat // LINUX: ret i32 (i32)* @_ZN2ns3fooEi.arch_sandybridge // LINUX: ret i32 (i32)* @_ZN2ns3fooEi.arch_ivybridge // LINUX: ret i32 (i32)* @_ZN2ns3fooEi.sse4.2 // LINUX: ret i32 (i32)* @_ZN2ns3fooEi -// WINDOWS: define dso_local i32 @"?foo@ns@@YAHH@Z.resolver"(i32 %0) comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo@ns@@YAHH@Z.resolver"(i32 %0) comdat // WINDOWS: call i32 @"?foo@ns@@YAHH@Z.arch_sandybridge"(i32 %0) // WINDOWS: call i32 @"?foo@ns@@YAHH@Z.arch_ivybridge"(i32 %0) // WINDOWS: call i32 @"?foo@ns@@YAHH@Z.sse4.2"(i32 %0) diff --git a/test/CodeGenCXX/attr-target-mv-inalloca.cpp b/test/CodeGenCXX/attr-target-mv-inalloca.cpp index 1fb2b11da3..a611587b56 100644 --- a/test/CodeGenCXX/attr-target-mv-inalloca.cpp +++ b/test/CodeGenCXX/attr-target-mv-inalloca.cpp @@ -41,7 +41,7 @@ void usage() { // WINDOWS: %[[ARGMEM:[0-9a-zA-Z]+]] = alloca inalloca <{ %struct.Foo }> // WINDOWS: %[[CALL:[0-9a-zA-Z]+]] = call i32 @"?bar@@YAHUFoo@@@Z.resolver"(<{ %struct.Foo }>* inalloca %[[ARGMEM]]) -// WINDOWS: define dso_local i32 @"?bar@@YAHUFoo@@@Z.resolver"(<{ %struct.Foo }>* %0) +// WINDOWS: define weak_odr dso_local i32 @"?bar@@YAHUFoo@@@Z.resolver"(<{ %struct.Foo }>* %0) // WINDOWS: %[[RET:[0-9a-zA-Z]+]] = musttail call i32 @"?bar@@YAHUFoo@@@Z.arch_ivybridge"(<{ %struct.Foo }>* %0) // WINDOWS-NEXT: ret i32 %[[RET]] // WINDOWS: %[[RET:[0-9a-zA-Z]+]] = musttail call i32 @"?bar@@YAHUFoo@@@Z.sse4.2"(<{ %struct.Foo }>* %0) @@ -72,7 +72,7 @@ void usage() { // WINDOWS64: %[[ARG:[0-9a-zA-Z.]+]] = alloca %struct.Foo // WINDOWS64: %[[CALL:[0-9a-zA-Z]+]] = call i32 @"?bar@@YAHUFoo@@@Z.resolver"(%struct.Foo* %[[ARG]]) -// WINDOWS64: define dso_local i32 @"?bar@@YAHUFoo@@@Z.resolver"(%struct.Foo* %0) +// WINDOWS64: define weak_odr dso_local i32 @"?bar@@YAHUFoo@@@Z.resolver"(%struct.Foo* %0) // WINDOWS64: %[[RET:[0-9a-zA-Z]+]] = musttail call i32 @"?bar@@YAHUFoo@@@Z.arch_ivybridge"(%struct.Foo* %0) // WINDOWS64-NEXT: ret i32 %[[RET]] // WINDOWS64: %[[RET:[0-9a-zA-Z]+]] = musttail call i32 @"?bar@@YAHUFoo@@@Z.sse4.2"(%struct.Foo* %0) diff --git a/test/CodeGenCXX/attr-target-mv-member-funcs.cpp b/test/CodeGenCXX/attr-target-mv-member-funcs.cpp index 6a8aa8cfa7..692a250d83 100644 --- a/test/CodeGenCXX/attr-target-mv-member-funcs.cpp +++ b/test/CodeGenCXX/attr-target-mv-member-funcs.cpp @@ -65,13 +65,13 @@ int templ_use() { return a.foo(1) + b.foo(2); } -// LINUX: @_ZN1SaSERKS_.ifunc = ifunc %struct.S* (%struct.S*, %struct.S*), %struct.S* (%struct.S*, %struct.S*)* ()* @_ZN1SaSERKS_.resolver -// LINUX: @_ZNK9ConvertTocv1SEv.ifunc = ifunc void (%struct.ConvertTo*), void (%struct.ConvertTo*)* ()* @_ZNK9ConvertTocv1SEv.resolver -// LINUX: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver -// LINUX: @_ZN2S23fooEi.ifunc = ifunc i32 (%struct.S2*, i32), i32 (%struct.S2*, i32)* ()* @_ZN2S23fooEi.resolver +// LINUX: @_ZN1SaSERKS_.ifunc = weak_odr ifunc %struct.S* (%struct.S*, %struct.S*), %struct.S* (%struct.S*, %struct.S*)* ()* @_ZN1SaSERKS_.resolver +// LINUX: @_ZNK9ConvertTocv1SEv.ifunc = weak_odr ifunc void (%struct.ConvertTo*), void (%struct.ConvertTo*)* ()* @_ZNK9ConvertTocv1SEv.resolver +// LINUX: @_ZN1S3fooEi.ifunc = weak_odr ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver +// LINUX: @_ZN2S23fooEi.ifunc = weak_odr ifunc i32 (%struct.S2*, i32), i32 (%struct.S2*, i32)* ()* @_ZN2S23fooEi.resolver // Templates: -// LINUX: @_ZN5templIiE3fooEi.ifunc = ifunc i32 (%struct.templ*, i32), i32 (%struct.templ*, i32)* ()* @_ZN5templIiE3fooEi.resolver -// LINUX: @_ZN5templIdE3fooEi.ifunc = ifunc i32 (%struct.templ.0*, i32), i32 (%struct.templ.0*, i32)* ()* @_ZN5templIdE3fooEi.resolver +// LINUX: @_ZN5templIiE3fooEi.ifunc = weak_odr ifunc i32 (%struct.templ*, i32), i32 (%struct.templ*, i32)* ()* @_ZN5templIiE3fooEi.resolver +// LINUX: @_ZN5templIdE3fooEi.ifunc = weak_odr ifunc i32 (%struct.templ.0*, i32), i32 (%struct.templ.0*, i32)* ()* @_ZN5templIdE3fooEi.resolver // LINUX: define i32 @_Z3barv() // LINUX: %s = alloca %struct.S, align 1 @@ -91,29 +91,29 @@ int templ_use() { // WINDOWS: call dereferenceable(1) %struct.S* @"??4S@@QEAAAEAU0@AEBU0@@Z.resolver"(%struct.S* %s2 // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %s, i32 0) -// LINUX: define %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.resolver() comdat +// LINUX: define weak_odr %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.resolver() comdat // LINUX: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_.arch_ivybridge // LINUX: ret %struct.S* (%struct.S*, %struct.S*)* @_ZN1SaSERKS_ -// WINDOWS: define dso_local %struct.S* @"??4S@@QEAAAEAU0@AEBU0@@Z.resolver"(%struct.S* %0, %struct.S* %1) +// WINDOWS: define weak_odr dso_local %struct.S* @"??4S@@QEAAAEAU0@AEBU0@@Z.resolver"(%struct.S* %0, %struct.S* %1) // WINDOWS: call %struct.S* @"??4S@@QEAAAEAU0@AEBU0@@Z.arch_ivybridge" // WINDOWS: call %struct.S* @"??4S@@QEAAAEAU0@AEBU0@@Z" -// LINUX: define void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.resolver() comdat +// LINUX: define weak_odr void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.resolver() comdat // LINUX: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv.arch_ivybridge // LINUX: ret void (%struct.ConvertTo*)* @_ZNK9ConvertTocv1SEv -// WINDOWS: define dso_local void @"??BConvertTo@@QEBA?AUS@@XZ.resolver"(%struct.ConvertTo* %0, %struct.S* %1) +// WINDOWS: define weak_odr dso_local void @"??BConvertTo@@QEBA?AUS@@XZ.resolver"(%struct.ConvertTo* %0, %struct.S* %1) // WINDOWS: call void @"??BConvertTo@@QEBA?AUS@@XZ.arch_ivybridge" // WINDOWS: call void @"??BConvertTo@@QEBA?AUS@@XZ" -// LINUX: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver() comdat +// LINUX: define weak_odr i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver() comdat // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2 // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi -// WINDOWS: define dso_local i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %0, i32 %1) +// WINDOWS: define weak_odr dso_local i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %0, i32 %1) // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.arch_sandybridge" // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.arch_ivybridge" // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.sse4.2" @@ -125,13 +125,13 @@ int templ_use() { // WINDOWS: define dso_local i32 @"?bar2@@YAHXZ"() // WINDOWS: call i32 @"?foo@S2@@QEAAHH@Z.resolver" -// LINUX: define i32 (%struct.S2*, i32)* @_ZN2S23fooEi.resolver() comdat +// LINUX: define weak_odr i32 (%struct.S2*, i32)* @_ZN2S23fooEi.resolver() comdat // LINUX: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_sandybridge // LINUX: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.arch_ivybridge // LINUX: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi.sse4.2 // LINUX: ret i32 (%struct.S2*, i32)* @_ZN2S23fooEi -// WINDOWS: define dso_local i32 @"?foo@S2@@QEAAHH@Z.resolver"(%struct.S2* %0, i32 %1) +// WINDOWS: define weak_odr dso_local i32 @"?foo@S2@@QEAAHH@Z.resolver"(%struct.S2* %0, i32 %1) // WINDOWS: call i32 @"?foo@S2@@QEAAHH@Z.arch_sandybridge" // WINDOWS: call i32 @"?foo@S2@@QEAAHH@Z.arch_ivybridge" // WINDOWS: call i32 @"?foo@S2@@QEAAHH@Z.sse4.2" @@ -153,25 +153,25 @@ int templ_use() { // WINDOWS: call i32 @"?foo@?$templ@H@@QEAAHH@Z.resolver" // WINDOWS: call i32 @"?foo@?$templ@N@@QEAAHH@Z.resolver" -// LINUX: define i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.resolver() comdat +// LINUX: define weak_odr i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.resolver() comdat // LINUX: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_sandybridge // LINUX: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.arch_ivybridge // LINUX: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi.sse4.2 // LINUX: ret i32 (%struct.templ*, i32)* @_ZN5templIiE3fooEi -// WINDOWS: define dso_local i32 @"?foo@?$templ@H@@QEAAHH@Z.resolver"(%struct.templ* %0, i32 %1) +// WINDOWS: define weak_odr dso_local i32 @"?foo@?$templ@H@@QEAAHH@Z.resolver"(%struct.templ* %0, i32 %1) // WINDOWS: call i32 @"?foo@?$templ@H@@QEAAHH@Z.arch_sandybridge" // WINDOWS: call i32 @"?foo@?$templ@H@@QEAAHH@Z.arch_ivybridge" // WINDOWS: call i32 @"?foo@?$templ@H@@QEAAHH@Z.sse4.2" // WINDOWS: call i32 @"?foo@?$templ@H@@QEAAHH@Z" -// LINUX: define i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.resolver() comdat +// LINUX: define weak_odr i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.resolver() comdat // LINUX: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_sandybridge // LINUX: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.arch_ivybridge // LINUX: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi.sse4.2 // LINUX: ret i32 (%struct.templ.0*, i32)* @_ZN5templIdE3fooEi -// WINDOWS: define dso_local i32 @"?foo@?$templ@N@@QEAAHH@Z.resolver"(%struct.templ.0* %0, i32 %1) comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo@?$templ@N@@QEAAHH@Z.resolver"(%struct.templ.0* %0, i32 %1) comdat // WINDOWS: call i32 @"?foo@?$templ@N@@QEAAHH@Z.arch_sandybridge" // WINDOWS: call i32 @"?foo@?$templ@N@@QEAAHH@Z.arch_ivybridge" // WINDOWS: call i32 @"?foo@?$templ@N@@QEAAHH@Z.sse4.2" diff --git a/test/CodeGenCXX/attr-target-mv-modules.cpp b/test/CodeGenCXX/attr-target-mv-modules.cpp index 6ff2046831..0fa15bfedf 100644 --- a/test/CodeGenCXX/attr-target-mv-modules.cpp +++ b/test/CodeGenCXX/attr-target-mv-modules.cpp @@ -22,7 +22,7 @@ __attribute__((target("sse4.2"))) void f(); void g() { f(); } // Negative tests to validate that the resolver only calls each 1x. -// CHECK: define void ()* @_Z1fv.resolver +// CHECK: define weak_odr void ()* @_Z1fv.resolver // CHECK: ret void ()* @_Z1fv.sse4.2 // CHECK-NOT: ret void ()* @_Z1fv.sse4.2 // CHECK: ret void ()* @_Z1fv diff --git a/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp b/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp index 7952544f93..1b9fb8f01e 100644 --- a/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp +++ b/test/CodeGenCXX/attr-target-mv-out-of-line-defs.cpp @@ -16,7 +16,7 @@ int bar() { return s.foo(0); } -// LINUX: @_ZN1S3fooEi.ifunc = ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver +// LINUX: @_ZN1S3fooEi.ifunc = weak_odr ifunc i32 (%struct.S*, i32), i32 (%struct.S*, i32)* ()* @_ZN1S3fooEi.resolver // LINUX: define i32 @_ZN1S3fooEi(%struct.S* %this, i32 %0) // LINUX: ret i32 2 @@ -44,13 +44,13 @@ int bar() { // WINDOWS: %s = alloca %struct.S, align 1 // WINDOWS: %call = call i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %s, i32 0) -// LINUX: define i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver() comdat +// LINUX: define weak_odr i32 (%struct.S*, i32)* @_ZN1S3fooEi.resolver() comdat // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_sandybridge // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.arch_ivybridge // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi.sse4.2 // LINUX: ret i32 (%struct.S*, i32)* @_ZN1S3fooEi -// WINDOWS: define dso_local i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %0, i32 %1) comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo@S@@QEAAHH@Z.resolver"(%struct.S* %0, i32 %1) comdat // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.arch_sandybridge"(%struct.S* %0, i32 %1) // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.arch_ivybridge"(%struct.S* %0, i32 %1) // WINDOWS: call i32 @"?foo@S@@QEAAHH@Z.sse4.2"(%struct.S* %0, i32 %1) diff --git a/test/CodeGenCXX/attr-target-mv-overloads.cpp b/test/CodeGenCXX/attr-target-mv-overloads.cpp index 45db1ba0f1..75350fe885 100644 --- a/test/CodeGenCXX/attr-target-mv-overloads.cpp +++ b/test/CodeGenCXX/attr-target-mv-overloads.cpp @@ -14,8 +14,8 @@ int bar2() { return foo_overload() + foo_overload(1); } -// LINUX: @_Z12foo_overloadv.ifunc = ifunc i32 (), i32 ()* ()* @_Z12foo_overloadv.resolver -// LINUX: @_Z12foo_overloadi.ifunc = ifunc i32 (i32), i32 (i32)* ()* @_Z12foo_overloadi.resolver +// LINUX: @_Z12foo_overloadv.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @_Z12foo_overloadv.resolver +// LINUX: @_Z12foo_overloadi.ifunc = weak_odr ifunc i32 (i32), i32 (i32)* ()* @_Z12foo_overloadi.resolver // LINUX: define i32 @_Z12foo_overloadi.sse4.2(i32 %0) // LINUX: ret i32 0 @@ -51,25 +51,25 @@ int bar2() { // WINDOWS: call i32 @"?foo_overload@@YAHXZ.resolver"() // WINDOWS: call i32 @"?foo_overload@@YAHH@Z.resolver"(i32 1) -// LINUX: define i32 ()* @_Z12foo_overloadv.resolver() comdat +// LINUX: define weak_odr i32 ()* @_Z12foo_overloadv.resolver() comdat // LINUX: ret i32 ()* @_Z12foo_overloadv.arch_sandybridge // LINUX: ret i32 ()* @_Z12foo_overloadv.arch_ivybridge // LINUX: ret i32 ()* @_Z12foo_overloadv.sse4.2 // LINUX: ret i32 ()* @_Z12foo_overloadv -// WINDOWS: define dso_local i32 @"?foo_overload@@YAHXZ.resolver"() comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo_overload@@YAHXZ.resolver"() comdat // WINDOWS: call i32 @"?foo_overload@@YAHXZ.arch_sandybridge" // WINDOWS: call i32 @"?foo_overload@@YAHXZ.arch_ivybridge" // WINDOWS: call i32 @"?foo_overload@@YAHXZ.sse4.2" // WINDOWS: call i32 @"?foo_overload@@YAHXZ" -// LINUX: define i32 (i32)* @_Z12foo_overloadi.resolver() comdat +// LINUX: define weak_odr i32 (i32)* @_Z12foo_overloadi.resolver() comdat // LINUX: ret i32 (i32)* @_Z12foo_overloadi.arch_sandybridge // LINUX: ret i32 (i32)* @_Z12foo_overloadi.arch_ivybridge // LINUX: ret i32 (i32)* @_Z12foo_overloadi.sse4.2 // LINUX: ret i32 (i32)* @_Z12foo_overloadi -// WINDOWS: define dso_local i32 @"?foo_overload@@YAHH@Z.resolver"(i32 %0) comdat +// WINDOWS: define weak_odr dso_local i32 @"?foo_overload@@YAHH@Z.resolver"(i32 %0) comdat // WINDOWS: call i32 @"?foo_overload@@YAHH@Z.arch_sandybridge" // WINDOWS: call i32 @"?foo_overload@@YAHH@Z.arch_ivybridge" // WINDOWS: call i32 @"?foo_overload@@YAHH@Z.sse4.2"