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.import.bc -o - | FileCheck --check-prefix=OPT2 %s +; OPT2-NOT: @ + +; RUN: llvm-dis %t.o.import.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 @@ -740,16 +740,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); @@ -1003,6 +993,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 @@ -1018,6 +1011,16 @@ // Perform function importing. FunctionImporter Importer(*CombinedIndex, Loader); Importer.importFunctions(*M, *ImportList); + + // If save-temps is specified, save the module just after importing + // and index-based optimizations, but before the rest of the optimizations. + if (options::TheOutputType == options::OT_SAVE_TEMPS) { + // Always expect a provided filename in the ThinLTO backend. + assert(!SaveTempsFilename.empty()); + SmallString<128> OptFilename(SaveTempsFilename); + OptFilename += ".import.bc"; + saveBCFile(OptFilename, *M); + } } legacy::PassManager passes; @@ -1327,6 +1330,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) { @@ -1347,19 +1354,25 @@ 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). + // separately). 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; if (Resolution != LDPR_PREVAILING_DEF_IRONLY) Preserve.insert(GlobalValue::getGUID(Sym.name)); + + if (Index && (Resolution == LDPR_PREVAILING_DEF || + Resolution == LDPR_PREVAILING_DEF_IRONLY || + Resolution == LDPR_PREVAILING_DEF_IRONLY_EXP)) + PrevailingCopy[GlobalValue::getGUID(Sym.name)] = + Index->getGlobalValueSummary(GlobalValue::getGUID(Sym.name)); } + + // 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 -> @@ -1373,6 +1386,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). @@ -1383,6 +1402,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.