Index: test/tools/gold/X86/Inputs/thinlto_weak_resolution.ll =================================================================== --- /dev/null +++ test/tools/gold/X86/Inputs/thinlto_weak_resolution.ll @@ -0,0 +1,37 @@ +target triple = "x86_64-unknown-linux-gnu" + +; Alias are not optimized +@linkonceodralias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias + +; Alias are not optimized +@linkoncealias = linkonce alias void (), void ()* @linkoncefuncwithalias + +; Function with an alias are not optimized +define linkonce_odr void @linkonceodrfuncwithalias() #0 { +entry: + ret void +} + +; Function with an alias are not optimized +define linkonce void @linkoncefuncwithalias() #0 { +entry: + ret void +} + +define linkonce_odr void @linkonceodrfunc() #0 { +entry: + ret void +} +define linkonce void @linkoncefunc() #0 { +entry: + ret void +} +define weak_odr void @weakodrfunc() #0 { +entry: + ret void +} +define weak void @weakfunc() #0 { +entry: + ret void +} + Index: test/tools/gold/X86/thinlto_weak_resolution.ll =================================================================== --- /dev/null +++ test/tools/gold/X86/thinlto_weak_resolution.ll @@ -0,0 +1,87 @@ +; RUN: opt -module-summary %s -o %t.o +; RUN: opt -module-summary %p/Inputs/thinlto_weak_resolution.ll -o %t2.o + +; Verify that prevailing weak for linker symbol is kept. +; Note that gold picks the first copy of a function as the prevailing one, +; so listing %t.o first is sufficient to ensure that its copies are prevailing. +; RUN: %gold -m elf_x86_64 -plugin %llvmshlibdir/LLVMgold.so \ +; RUN: --plugin-opt=thinlto \ +; RUN: --plugin-opt=save-temps \ +; RUN: -shared \ +; RUN: -o %t3.o %t.o %t2.o + +; RUN: llvm-nm %t3.o | FileCheck %s +; CHECK: weakfunc + +; All of the preempted functions should have been eliminated (the plugin will +; not link them in). +; RUN: llvm-dis %t2.o.opt.bc -o - | FileCheck --check-prefix=OPT2 %s +; OPT2-NOT: @ + +; RUN: llvm-dis %t.o.opt.bc -o - | FileCheck --check-prefix=OPT %s + +target triple = "x86_64-unknown-linux-gnu" + + +define i32 @main() #0 { +entry: + call void @linkonceodralias() + call void @linkoncealias() + call void @linkonceodrfuncwithalias() + call void @linkoncefuncwithalias() + call void @linkonceodrfunc() + call void @linkoncefunc() + call void @weakodrfunc() + call void @weakfunc() + call void @linkonceodrfuncInSingleModule() + ret i32 0 +} + +; Alias are resolved +; OPT: @linkonceodralias = weak_odr alias void (), void ()* @linkonceodrfuncwithalias +@linkonceodralias = linkonce_odr alias void (), void ()* @linkonceodrfuncwithalias + +; Alias are resolved +; OPT: @linkoncealias = weak alias void (), void ()* @linkoncefuncwithalias +@linkoncealias = linkonce alias void (), void ()* @linkoncefuncwithalias + +; Function with an alias are not optimized +; OPT: define linkonce_odr void @linkonceodrfuncwithalias() +define linkonce_odr void @linkonceodrfuncwithalias() #0 { +entry: + ret void +} + +; Function with an alias are not optimized +; OPT: define linkonce void @linkoncefuncwithalias() +define linkonce void @linkoncefuncwithalias() #0 { +entry: + ret void +} + +; OPT: define weak_odr void @linkonceodrfunc() +define linkonce_odr void @linkonceodrfunc() #0 { +entry: + ret void +} +; OPT: define weak void @linkoncefunc() +define linkonce void @linkoncefunc() #0 { +entry: + ret void +} +; OPT: define weak_odr void @weakodrfunc() +define weak_odr void @weakodrfunc() #0 { +entry: + ret void +} +; OPT: define weak void @weakfunc() +define weak void @weakfunc() #0 { +entry: + ret void +} + +; OPT: weak_odr void @linkonceodrfuncInSingleModule() +define linkonce_odr void @linkonceodrfuncInSingleModule() #0 { +entry: + ret void +} Index: tools/gold/gold-plugin.cpp =================================================================== --- tools/gold/gold-plugin.cpp +++ tools/gold/gold-plugin.cpp @@ -735,16 +735,6 @@ if (Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP && !Res.IsLinkonceOdr) Resolution = LDPR_PREVAILING_DEF; - // In ThinLTO mode change all prevailing resolutions to LDPR_PREVAILING_DEF. - // For ThinLTO the IR files are compiled through the backend independently, - // so we need to ensure that any prevailing linkonce copy will be emitted - // into the object file by making it weak. Additionally, we can skip the - // IRONLY handling for internalization, which isn't performed in ThinLTO - // mode currently anyway. - if (options::thinlto && (Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP || - Resolution == LDPR_PREVAILING_DEF_IRONLY)) - Resolution = LDPR_PREVAILING_DEF; - GV->setUnnamedAddr(Res.UnnamedAddr); GV->setVisibility(Res.Visibility); @@ -998,6 +988,9 @@ M->setDataLayout(TM->createDataLayout()); if (CombinedIndex) { + // Apply summary-based LinkOnce/Weak resolution decisions. + thinLTOResolveWeakForLinkerModule(*M, *DefinedGlobals); + // Apply summary-based internalization decisions. Skip if there are no // defined globals from the summary since not only is it unnecessary, but // if this module did not have a summary section the internalizer will @@ -1322,6 +1315,10 @@ // are referenced outside of a single IR module. DenseSet Preserve; + // Keep track of the prevailing copy for each GUID, for use in resolving + // weak linkages. + DenseMap PrevailingCopy; + ModuleSummaryIndex CombinedIndex; uint64_t NextModuleId = 0; for (claimed_file &F : Modules) { @@ -1342,19 +1339,27 @@ std::unique_ptr Index = getModuleSummaryIndexForFile(F); - // Skip files without a module summary. - if (Index) - CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); - // Use gold's symbol resolution information to identify symbols referenced - // by more than a single IR module (before importing, which is checked - // separately). + // by more than a single IR module (i.e. referenced by multiple IR modules + // or by a non-IR module). Cross references introduced by importing are + // checked separately via the export lists. Also track the prevailing copy + // for later symbol resolution. for (auto &Sym : F.syms) { ld_plugin_symbol_resolution Resolution = (ld_plugin_symbol_resolution)Sym.resolution; + GlobalValue::GUID SymGUID = GlobalValue::getGUID(Sym.name); if (Resolution != LDPR_PREVAILING_DEF_IRONLY) - Preserve.insert(GlobalValue::getGUID(Sym.name)); + Preserve.insert(SymGUID); + + if (Index && (Resolution == LDPR_PREVAILING_DEF || + Resolution == LDPR_PREVAILING_DEF_IRONLY || + Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)) + PrevailingCopy[SymGUID] = Index->getGlobalValueSummary(SymGUID); } + + // Skip files without a module summary. + if (Index) + CombinedIndex.mergeFrom(std::move(Index), ++NextModuleId); } // Collect for each module the list of function it defines (GUID -> @@ -1368,6 +1373,12 @@ ComputeCrossModuleImport(CombinedIndex, ModuleToDefinedGVSummaries, ImportLists, ExportLists); + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { + const auto &Prevailing = PrevailingCopy.find(GUID); + assert(Prevailing != PrevailingCopy.end()); + return Prevailing->second == S; + }; + // Callback for internalization, to prevent internalization of symbols // that were not candidates initially, and those that are being imported // (which introduces new cross references). @@ -1378,6 +1389,11 @@ Preserve.count(GUID); }; + thinLTOResolveWeakForLinkerInIndex( + CombinedIndex, isPrevailing, + [](StringRef ModuleIdentifier, GlobalValue::GUID GUID, + GlobalValue::LinkageTypes NewLinkage) {}); + // Use global summary-based analysis to identify symbols that can be // internalized (because they aren't exported or preserved as per callback). // Changes are made in the index, consumed in the ThinLTO backends.