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 @@ -1505,7 +1505,6 @@ void EmitAliasDefinition(GlobalDecl GD); void emitIFuncDefinition(GlobalDecl GD); void emitCPUDispatchDefinition(GlobalDecl GD); - void EmitTargetClonesResolver(GlobalDecl GD); void EmitObjCPropertyImplementations(const ObjCImplementationDecl *D); void EmitObjCIvarInitializations(ObjCImplementationDecl *D); 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 @@ -3320,13 +3320,13 @@ auto *Spec = FD->getAttr(); for (unsigned I = 0; I < Spec->cpus_size(); ++I) EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr); - // Requires multiple emits. } else if (FD->isTargetClonesMultiVersion()) { auto *Clone = FD->getAttr(); for (unsigned I = 0; I < Clone->featuresStrs_size(); ++I) if (Clone->isFirstOfVersion(I)) EmitGlobalFunctionDefinition(GD.getWithMultiVersionIndex(I), nullptr); - EmitTargetClonesResolver(GD); + // Ensure that the resolver function is also emitted. + GetOrCreateMultiVersionResolver(GD); } else EmitGlobalFunctionDefinition(GD, GV); } @@ -3408,57 +3408,6 @@ return llvm::GlobalValue::WeakODRLinkage; } -void CodeGenModule::EmitTargetClonesResolver(GlobalDecl GD) { - const auto *FD = cast(GD.getDecl()); - assert(FD && "Not a FunctionDecl?"); - const auto *TC = FD->getAttr(); - assert(TC && "Not a target_clones Function?"); - - llvm::Function *ResolverFunc; - if (getTarget().supportsIFunc()) { - auto *IFunc = cast(GetOrCreateMultiVersionResolver(GD)); - ResolverFunc = cast(IFunc->getResolver()); - } else - ResolverFunc = cast(GetOrCreateMultiVersionResolver(GD)); - - SmallVector Options; - for (unsigned VersionIndex = 0; VersionIndex < TC->featuresStrs_size(); - ++VersionIndex) { - if (!TC->isFirstOfVersion(VersionIndex)) - continue; - StringRef Version = TC->getFeatureStr(VersionIndex); - StringRef MangledName = - getMangledName(GD.getWithMultiVersionIndex(VersionIndex)); - llvm::Constant *Func = GetGlobalValue(MangledName); - assert(Func && - "Should have already been created before calling resolver emit"); - - StringRef Architecture; - llvm::SmallVector Feature; - - if (Version.startswith("arch=")) - Architecture = Version.drop_front(sizeof("arch=") - 1); - else if (Version != "default") - Feature.push_back(Version); - - Options.emplace_back(cast(Func), Architecture, Feature); - } - - if (supportsCOMDAT()) - ResolverFunc->setComdat( - getModule().getOrInsertComdat(ResolverFunc->getName())); - - const TargetInfo &TI = getTarget(); - std::stable_sort( - Options.begin(), Options.end(), - [&TI](const CodeGenFunction::MultiVersionResolverOption &LHS, - const CodeGenFunction::MultiVersionResolverOption &RHS) { - return TargetMVPriority(TI, LHS) > TargetMVPriority(TI, RHS); - }); - CodeGenFunction CGF(*this); - CGF.EmitMultiVersionResolver(ResolverFunc, Options); -} - void CodeGenModule::emitMultiVersionFunctions() { std::vector MVFuncsToEmit; MultiVersionFuncs.swap(MVFuncsToEmit); @@ -3495,26 +3444,58 @@ Options.emplace_back(cast(Func), TA->getArchitecture(), Feats); }); + } else if (FD->isTargetClonesMultiVersion()) { + const auto *TC = FD->getAttr(); + for (unsigned VersionIndex = 0; VersionIndex < TC->featuresStrs_size(); + ++VersionIndex) { + if (!TC->isFirstOfVersion(VersionIndex)) + continue; + GlobalDecl CurGD{(FD->isDefined() ? FD->getDefinition() : FD), + VersionIndex}; + StringRef Version = TC->getFeatureStr(VersionIndex); + StringRef MangledName = getMangledName(CurGD); + llvm::Constant *Func = GetGlobalValue(MangledName); + if (!Func) { + if (FD->isDefined()) { + EmitGlobalFunctionDefinition(CurGD, nullptr); + Func = GetGlobalValue(MangledName); + } else { + const CGFunctionInfo &FI = + getTypes().arrangeGlobalDeclaration(CurGD); + llvm::FunctionType *Ty = getTypes().GetFunctionType(FI); + Func = GetAddrOfFunction(CurGD, Ty, /*ForVTable=*/false, + /*DontDefer=*/false, ForDefinition); + } + assert(Func && "This should have just been created"); + } + + StringRef Architecture; + llvm::SmallVector Feature; + + if (Version.startswith("arch=")) + Architecture = Version.drop_front(sizeof("arch=") - 1); + else if (Version != "default") + Feature.push_back(Version); + + Options.emplace_back(cast(Func), Architecture, Feature); + } } else { - assert(0 && "Expected a target multiversion function"); + assert(0 && "Expected a target or target_clones multiversion function"); continue; } - llvm::Function *ResolverFunc; - const TargetInfo &TI = getTarget(); + llvm::Constant *ResolverConstant = GetOrCreateMultiVersionResolver(GD); + if (auto *IFunc = dyn_cast(ResolverConstant)) + ResolverConstant = IFunc->getResolver(); + llvm::Function *ResolverFunc = cast(ResolverConstant); - if (TI.supportsIFunc() || FD->isTargetMultiVersion()) { - ResolverFunc = cast( - GetGlobalValue((getMangledName(GD) + ".resolver").str())); - ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); - } else { - ResolverFunc = cast(GetGlobalValue(getMangledName(GD))); - } + ResolverFunc->setLinkage(getMultiversionLinkage(*this, GD)); if (supportsCOMDAT()) ResolverFunc->setComdat( getModule().getOrInsertComdat(ResolverFunc->getName())); + const TargetInfo &TI = getTarget(); llvm::stable_sort( Options, [&TI](const CodeGenFunction::MultiVersionResolverOption &LHS, const CodeGenFunction::MultiVersionResolverOption &RHS) { @@ -3676,35 +3657,17 @@ else if (FD->isTargetMultiVersion()) ResolverName += ".resolver"; - // If this already exists, just return that one. + // If the resolver has already been created, just return it. if (llvm::GlobalValue *ResolverGV = GetGlobalValue(ResolverName)) return ResolverGV; const CGFunctionInfo &FI = getTypes().arrangeGlobalDeclaration(GD); llvm::FunctionType *DeclTy = getTypes().GetFunctionType(FI); - // Since this is the first time we've created this IFunc, make sure - // that we put this multiversioned function into the list to be - // replaced later if necessary (target multiversioning only). - if (FD->isTargetMultiVersion()) + // The resolver needs to be created. For target and target_clones, defer + // creation until the end of the TU. + if (FD->isTargetMultiVersion() || FD->isTargetClonesMultiVersion()) MultiVersionFuncs.push_back(GD); - else if (FD->isTargetClonesMultiVersion()) { - // In target_clones multiversioning, make sure we emit this if used. - auto DDI = - DeferredDecls.find(getMangledName(GD.getWithMultiVersionIndex(0))); - if (DDI != DeferredDecls.end()) { - addDeferredDeclToEmit(GD); - DeferredDecls.erase(DDI); - } else { - // Emit the symbol of the 1st variant, so that the deferred decls know we - // need it, otherwise the only global value will be the resolver/ifunc, - // which end up getting broken if we search for them with GetGlobalValue'. - GetOrCreateLLVMFunction( - getMangledName(GD.getWithMultiVersionIndex(0)), DeclTy, FD, - /*ForVTable=*/false, /*DontDefer=*/true, - /*IsThunk=*/false, llvm::AttributeList(), ForDefinition); - } - } // For cpu_specific, don't create an ifunc yet because we don't know if the // cpu_dispatch will be emitted in this translation unit. diff --git a/clang/test/CodeGen/attr-target-clones.c b/clang/test/CodeGen/attr-target-clones.c --- a/clang/test/CodeGen/attr-target-clones.c +++ b/clang/test/CodeGen/attr-target-clones.c @@ -18,30 +18,31 @@ // LINUX: @unused.ifunc = weak_odr ifunc void (), void ()* ()* @unused.resolver // LINUX: @foo_inline.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo_inline.resolver // LINUX: @foo_inline2.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo_inline2.resolver +// LINUX: @foo_used_no_defn.ifunc = weak_odr ifunc i32 (), i32 ()* ()* @foo_used_no_defn.resolver int __attribute__((target_clones("sse4.2, default"))) foo(void) { return 0; } // LINUX: define {{.*}}i32 @foo.sse4.2.0() // LINUX: define {{.*}}i32 @foo.default.1() -// LINUX: define i32 ()* @foo.resolver() comdat +// LINUX: define weak_odr i32 ()* @foo.resolver() comdat // LINUX: ret i32 ()* @foo.sse4.2.0 // LINUX: ret i32 ()* @foo.default.1 // WINDOWS: define dso_local i32 @foo.sse4.2.0() // WINDOWS: define dso_local i32 @foo.default.1() -// WINDOWS: define dso_local i32 @foo() comdat +// WINDOWS: define weak_odr dso_local i32 @foo() comdat // WINDOWS: musttail call i32 @foo.sse4.2.0 // WINDOWS: musttail call i32 @foo.default.1 __attribute__((target_clones("default,default ,sse4.2"))) void foo_dupes(void) {} // LINUX: define {{.*}}void @foo_dupes.default.1() // LINUX: define {{.*}}void @foo_dupes.sse4.2.0() -// LINUX: define void ()* @foo_dupes.resolver() comdat +// LINUX: define weak_odr void ()* @foo_dupes.resolver() comdat // LINUX: ret void ()* @foo_dupes.sse4.2.0 // LINUX: ret void ()* @foo_dupes.default.1 // WINDOWS: define dso_local void @foo_dupes.default.1() // WINDOWS: define dso_local void @foo_dupes.sse4.2.0() -// WINDOWS: define dso_local void @foo_dupes() comdat +// WINDOWS: define weak_odr dso_local void @foo_dupes() comdat // WINDOWS: musttail call void @foo_dupes.sse4.2.0 // WINDOWS: musttail call void @foo_dupes.default.1 @@ -64,13 +65,13 @@ void __attribute__((target_clones("default, arch=ivybridge"))) unused(void) {} // LINUX: define {{.*}}void @unused.default.1() // LINUX: define {{.*}}void @unused.arch_ivybridge.0() -// LINUX: define void ()* @unused.resolver() comdat +// LINUX: define weak_odr void ()* @unused.resolver() comdat // LINUX: ret void ()* @unused.arch_ivybridge.0 // LINUX: ret void ()* @unused.default.1 // WINDOWS: define dso_local void @unused.default.1() // WINDOWS: define dso_local void @unused.arch_ivybridge.0() -// WINDOWS: define dso_local void @unused() comdat +// WINDOWS: define weak_odr dso_local void @unused() comdat // WINDOWS: musttail call void @unused.arch_ivybridge.0 // WINDOWS: musttail call void @unused.default.1 @@ -90,46 +91,80 @@ // WINDOWS: call i32 @foo_inline2() } -// Deferred emission of foo_inline, which got delayed because it is inline. -// LINUX: define i32 ()* @foo_inline.resolver() comdat +// LINUX: define weak_odr i32 ()* @foo_inline.resolver() comdat // LINUX: ret i32 ()* @foo_inline.arch_sandybridge.0 // LINUX: ret i32 ()* @foo_inline.sse4.2.1 // LINUX: ret i32 ()* @foo_inline.default.2 -// WINDOWS: define dso_local i32 @foo_inline() comdat +// WINDOWS: define weak_odr dso_local i32 @foo_inline() comdat // WINDOWS: musttail call i32 @foo_inline.arch_sandybridge.0 // WINDOWS: musttail call i32 @foo_inline.sse4.2.1 // WINDOWS: musttail call i32 @foo_inline.default.2 inline int __attribute__((target_clones("arch=sandybridge,default,sse4.2"))) foo_inline2(void){ return 0; } -// LINUX: define linkonce i32 @foo_inline2.arch_sandybridge.0() #[[SB:[0-9]+]] -// LINUX: define i32 ()* @foo_inline2.resolver() comdat +// LINUX: define weak_odr i32 ()* @foo_inline2.resolver() comdat // LINUX: ret i32 ()* @foo_inline2.arch_sandybridge.0 // LINUX: ret i32 ()* @foo_inline2.sse4.2.1 // LINUX: ret i32 ()* @foo_inline2.default.2 -// WINDOWS: define linkonce_odr dso_local i32 @foo_inline2.arch_sandybridge.0() #[[SB:[0-9]+]] -// WINDOWS: define dso_local i32 @foo_inline2() comdat +// WINDOWS: define weak_odr dso_local i32 @foo_inline2() comdat // WINDOWS: musttail call i32 @foo_inline2.arch_sandybridge.0 // WINDOWS: musttail call i32 @foo_inline2.sse4.2.1 // WINDOWS: musttail call i32 @foo_inline2.default.2 -// LINUX: define linkonce i32 @foo_inline.arch_sandybridge.0() #[[SB]] -// LINUX: define linkonce i32 @foo_inline.default.2() #[[DEF]] + +int __attribute__((target_clones("default", "sse4.2"))) +foo_unused_no_defn(void); + +int __attribute__((target_clones("default", "sse4.2"))) +foo_used_no_defn(void); + +int test_foo_used_no_defn(void) { + // LINUX: define {{.*}}i32 @test_foo_used_no_defn() + // WINDOWS: define dso_local i32 @test_foo_used_no_defn() + return foo_used_no_defn(); + // LINUX: call i32 @foo_used_no_defn.ifunc() + // WINDOWS: call i32 @foo_used_no_defn() +} + + +// LINUX: define weak_odr i32 ()* @foo_used_no_defn.resolver() comdat +// LINUX: ret i32 ()* @foo_used_no_defn.sse4.2.0 +// LINUX: ret i32 ()* @foo_used_no_defn.default.1 + +// WINDOWS: define weak_odr dso_local i32 @foo_used_no_defn() comdat +// WINDOWS: musttail call i32 @foo_used_no_defn.sse4.2.0 +// WINDOWS: musttail call i32 @foo_used_no_defn.default.1 + + +// Deferred emission of inline definitions. + +// LINUX: define linkonce i32 @foo_inline.arch_sandybridge.0() #[[SB:[0-9]+]] +// LINUX: define linkonce i32 @foo_inline.default.2() #[[DEF:[0-9]+]] // LINUX: define linkonce i32 @foo_inline.sse4.2.1() #[[SSE42:[0-9]+]] -// WINDOWS: define linkonce_odr dso_local i32 @foo_inline.arch_sandybridge.0() #[[SB]] +// WINDOWS: define linkonce_odr dso_local i32 @foo_inline.arch_sandybridge.0() #[[SB:[0-9]+]] // WINDOWS: define linkonce_odr dso_local i32 @foo_inline.default.2() #[[DEF]] // WINDOWS: define linkonce_odr dso_local i32 @foo_inline.sse4.2.1() #[[SSE42:[0-9]+]] +// LINUX: define linkonce i32 @foo_inline2.arch_sandybridge.0() #[[SB]] // LINUX: define linkonce i32 @foo_inline2.default.2() #[[DEF]] // LINUX: define linkonce i32 @foo_inline2.sse4.2.1() #[[SSE42]] +// WINDOWS: define linkonce_odr dso_local i32 @foo_inline2.arch_sandybridge.0() #[[SB]] // WINDOWS: define linkonce_odr dso_local i32 @foo_inline2.default.2() #[[DEF]] // WINDOWS: define linkonce_odr dso_local i32 @foo_inline2.sse4.2.1() #[[SSE42]] + +// LINUX: declare i32 @foo_used_no_defn.default.1() +// LINUX: declare i32 @foo_used_no_defn.sse4.2.0() + +// WINDOWS: declare dso_local i32 @foo_used_no_defn.default.1() +// WINDOWS: declare dso_local i32 @foo_used_no_defn.sse4.2.0() + + // CHECK: attributes #[[SSE42]] = // CHECK-SAME: "target-features"="+crc32,+cx8,+mmx,+popcnt,+sse,+sse2,+sse3,+sse4.1,+sse4.2,+ssse3,+x87" // CHECK: attributes #[[DEF]] = diff --git a/clang/test/CodeGenCXX/attr-target-clones.cpp b/clang/test/CodeGenCXX/attr-target-clones.cpp --- a/clang/test/CodeGenCXX/attr-target-clones.cpp +++ b/clang/test/CodeGenCXX/attr-target-clones.cpp @@ -12,29 +12,29 @@ int __attribute__((target_clones("sse4.2", "default"))) overloaded(int) { return 1; } // LINUX: define {{.*}}i32 @_Z10overloadedi.sse4.2.0(i32{{.+}}) // LINUX: define {{.*}}i32 @_Z10overloadedi.default.1(i32{{.+}}) -// LINUX: define i32 (i32)* @_Z10overloadedi.resolver +// LINUX: define weak_odr i32 (i32)* @_Z10overloadedi.resolver() comdat // LINUX: ret i32 (i32)* @_Z10overloadedi.sse4.2.0 // LINUX: ret i32 (i32)* @_Z10overloadedi.default.1 // WINDOWS: define dso_local noundef i32 @"?overloaded@@YAHH@Z.sse4.2.0"(i32{{.+}}) // WINDOWS: define dso_local noundef i32 @"?overloaded@@YAHH@Z.default.1"(i32{{.+}}) -// WINDOWS: define dso_local i32 @"?overloaded@@YAHH@Z"(i32{{.+}}) +// WINDOWS: define weak_odr dso_local i32 @"?overloaded@@YAHH@Z"(i32{{.+}}) comdat // WINDOWS: call i32 @"?overloaded@@YAHH@Z.sse4.2.0" // WINDOWS: call i32 @"?overloaded@@YAHH@Z.default.1" int __attribute__((target_clones("arch=ivybridge", "default"))) overloaded(const char *) { return 2; } // LINUX: define {{.*}}i32 @_Z10overloadedPKc.arch_ivybridge.0(i8*{{.+}}) // LINUX: define {{.*}}i32 @_Z10overloadedPKc.default.1(i8*{{.+}}) -// LINUX: define i32 (i8*)* @_Z10overloadedPKc.resolver +// LINUX: define weak_odr i32 (i8*)* @_Z10overloadedPKc.resolver() comdat // LINUX: ret i32 (i8*)* @_Z10overloadedPKc.arch_ivybridge.0 // LINUX: ret i32 (i8*)* @_Z10overloadedPKc.default.1 // WINDOWS: define dso_local noundef i32 @"?overloaded@@YAHPEBD@Z.arch_ivybridge.0"(i8*{{.+}}) // WINDOWS: define dso_local noundef i32 @"?overloaded@@YAHPEBD@Z.default.1"(i8*{{.+}}) -// WINDOWS: define dso_local i32 @"?overloaded@@YAHPEBD@Z"(i8*{{.+}}) +// WINDOWS: define weak_odr dso_local i32 @"?overloaded@@YAHPEBD@Z"(i8*{{.+}}) comdat // WINDOWS: call i32 @"?overloaded@@YAHPEBD@Z.arch_ivybridge.0" // WINDOWS: call i32 @"?overloaded@@YAHPEBD@Z.default.1" -// + void use_overloaded() { overloaded(1); // LINUX: call noundef i32 @_Z10overloadedi.ifunc @@ -81,36 +81,40 @@ // WINDOWS: call noundef i32 @"?foo@?$C@NM@@QEAAHXZ"(%struct.C } -// LINUX: define {{.*}}i32 @_ZN1CIssE3fooEv.sse4.2.0(%struct.C{{.+}}) -// WINDOWS: define {{.*}}i32 @"?foo@?$C@FF@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) -// LINUX: define i32 (%struct.C*)* @_ZN1CIssE3fooEv.resolver +// LINUX: define weak_odr i32 (%struct.C*)* @_ZN1CIssE3fooEv.resolver() comdat // LINUX: ret i32 (%struct.C*)* @_ZN1CIssE3fooEv.sse4.2.0 // LINUX: ret i32 (%struct.C*)* @_ZN1CIssE3fooEv.default.1 + // WINDOWS: define {{.*}}i32 @"?foo@?$C@FF@@QEAAHXZ"(%struct.C{{.+}}) // WINDOWS: call i32 @"?foo@?$C@FF@@QEAAHXZ.sse4.2.0" // WINDOWS: call i32 @"?foo@?$C@FF@@QEAAHXZ.default.1" -// LINUX: define {{.*}}i32 @_ZN1CIisE3fooEv.sse4.2.0(%struct.C{{.+}}) -// WINDOWS: define {{.*}}i32 @"?foo@?$C@HF@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) -// LINUX: define i32 (%struct.C{{.+}})* @_ZN1CIisE3fooEv.resolver +// LINUX: define weak_odr i32 (%struct.C{{.+}})* @_ZN1CIisE3fooEv.resolver() comdat // LINUX: ret i32 (%struct.C{{.+}})* @_ZN1CIisE3fooEv.sse4.2.0 // LINUX: ret i32 (%struct.C{{.+}})* @_ZN1CIisE3fooEv.default.1 + // WINDOWS: define {{.*}}i32 @"?foo@?$C@HF@@QEAAHXZ"(%struct.C{{.+}}) // WINDOWS: call i32 @"?foo@?$C@HF@@QEAAHXZ.sse4.2.0" // WINDOWS: call i32 @"?foo@?$C@HF@@QEAAHXZ.default.1" -// LINUX: define i32 (%struct.C{{.+}})* @_ZN1CIdfE3fooEv.resolver +// LINUX: define weak_odr i32 (%struct.C{{.+}})* @_ZN1CIdfE3fooEv.resolver() comdat // LINUX: ret i32 (%struct.C{{.+}})* @_ZN1CIdfE3fooEv.sse4.2.0 // LINUX: ret i32 (%struct.C{{.+}})* @_ZN1CIdfE3fooEv.default.1 + // WINDOWS: define {{.*}}i32 @"?foo@?$C@NM@@QEAAHXZ"(%struct.C{{.+}}) // WINDOWS: call i32 @"?foo@?$C@NM@@QEAAHXZ.sse4.2.0" // WINDOWS: call i32 @"?foo@?$C@NM@@QEAAHXZ.default.1" +// LINUX: define {{.*}}i32 @_ZN1CIssE3fooEv.sse4.2.0(%struct.C{{.+}}) +// LINUX: define {{.*}}i32 @_ZN1CIssE3fooEv.default.1(%struct.C{{.+}}) +// LINUX: define {{.*}}i32 @_ZN1CIisE3fooEv.sse4.2.0(%struct.C{{.+}}) +// LINUX: define {{.*}}i32 @_ZN1CIisE3fooEv.default.1(%struct.C{{.+}}) // LINUX: define {{.*}}i32 @_ZN1CIdfE3fooEv.sse4.2.0(%struct.C{{.+}}) -// WINDOWS: define {{.*}}i32 @"?foo@?$C@NM@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) // LINUX: define {{.*}}i32 @_ZN1CIdfE3fooEv.default.1(%struct.C{{.+}}) -// WINDOWS: define {{.*}}i32 @"?foo@?$C@NM@@QEAAHXZ.default.1"(%struct.C{{.+}}) -// LINUX: define {{.*}}i32 @_ZN1CIssE3fooEv.default.1(%struct.C{{.+}}) + +// WINDOWS: define {{.*}}i32 @"?foo@?$C@FF@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) // WINDOWS: define {{.*}}i32 @"?foo@?$C@FF@@QEAAHXZ.default.1"(%struct.C{{.+}}) -// LINUX: define {{.*}}i32 @_ZN1CIisE3fooEv.default.1(%struct.C{{.+}}) +// WINDOWS: define {{.*}}i32 @"?foo@?$C@HF@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) // WINDOWS: define {{.*}}i32 @"?foo@?$C@HF@@QEAAHXZ.default.1"(%struct.C{{.+}}) +// WINDOWS: define {{.*}}i32 @"?foo@?$C@NM@@QEAAHXZ.sse4.2.0"(%struct.C{{.+}}) +// WINDOWS: define {{.*}}i32 @"?foo@?$C@NM@@QEAAHXZ.default.1"(%struct.C{{.+}})