Index: llvm/lib/LTO/LTO.cpp =================================================================== --- llvm/lib/LTO/LTO.cpp +++ llvm/lib/LTO/LTO.cpp @@ -1098,42 +1098,45 @@ ThinLTO.ModuleMap.size()); StringMap> ResolvedODR; - if (Conf.OptLevel > 0) { + if (Conf.OptLevel > 0) ComputeCrossModuleImport(ThinLTO.CombinedIndex, ModuleToDefinedGVSummaries, ImportLists, ExportLists); - std::set ExportedGUIDs; - for (auto &Res : GlobalResolutions) { - // First check if the symbol was flagged as having external references. - if (Res.second.Partition != GlobalResolution::External) - continue; - // IRName will be defined if we have seen the prevailing copy of - // this value. If not, no need to mark as exported from a ThinLTO - // partition (and we can't get the GUID). - if (Res.second.IRName.empty()) - continue; - auto GUID = GlobalValue::getGUID( - GlobalValue::dropLLVMManglingEscape(Res.second.IRName)); - // Mark exported unless index-based analysis determined it to be dead. - if (ThinLTO.CombinedIndex.isGUIDLive(GUID)) - ExportedGUIDs.insert(GUID); - } - - // Any functions referenced by the jump table in the regular LTO object must - // be exported. - for (auto &Def : ThinLTO.CombinedIndex.cfiFunctionDefs()) - ExportedGUIDs.insert( - GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Def))); - - auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { - const auto &ExportList = ExportLists.find(ModuleIdentifier); - return (ExportList != ExportLists.end() && - ExportList->second.count(GUID)) || - ExportedGUIDs.count(GUID); - }; - thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported); + // Figure out which symbols need to be internalized. This also needs to happen + // at -O0 because summary-based DCE is implemented using internalization, and + // we must apply DCE consistently with the full LTO module in order to avoid + // undefined references during the final link. + std::set ExportedGUIDs; + for (auto &Res : GlobalResolutions) { + // First check if the symbol was flagged as having external references. + if (Res.second.Partition != GlobalResolution::External) + continue; + // IRName will be defined if we have seen the prevailing copy of + // this value. If not, no need to mark as exported from a ThinLTO + // partition (and we can't get the GUID). + if (Res.second.IRName.empty()) + continue; + auto GUID = GlobalValue::getGUID( + GlobalValue::dropLLVMManglingEscape(Res.second.IRName)); + // Mark exported unless index-based analysis determined it to be dead. + if (ThinLTO.CombinedIndex.isGUIDLive(GUID)) + ExportedGUIDs.insert(GUID); } + // Any functions referenced by the jump table in the regular LTO object must + // be exported. + for (auto &Def : ThinLTO.CombinedIndex.cfiFunctionDefs()) + ExportedGUIDs.insert( + GlobalValue::getGUID(GlobalValue::dropLLVMManglingEscape(Def))); + + auto isExported = [&](StringRef ModuleIdentifier, GlobalValue::GUID GUID) { + const auto &ExportList = ExportLists.find(ModuleIdentifier); + return (ExportList != ExportLists.end() && + ExportList->second.count(GUID)) || + ExportedGUIDs.count(GUID); + }; + thinLTOInternalizeAndPromoteInIndex(ThinLTO.CombinedIndex, isExported); + auto isPrevailing = [&](GlobalValue::GUID GUID, const GlobalValueSummary *S) { return ThinLTO.PrevailingModuleForGUID[GUID] == S->modulePath(); Index: llvm/lib/Transforms/IPO/PassManagerBuilder.cpp =================================================================== --- llvm/lib/Transforms/IPO/PassManagerBuilder.cpp +++ llvm/lib/Transforms/IPO/PassManagerBuilder.cpp @@ -418,6 +418,14 @@ else if (GlobalExtensionsNotEmpty() || !Extensions.empty()) MPM.add(createBarrierNoopPass()); + if (PerformThinLTO) { + // Drop available_externally and unreferenced globals. This is necessary + // with ThinLTO in order to avoid leaving undefined references to dead + // globals in the object file. + MPM.add(createEliminateAvailableExternallyPass()); + MPM.add(createGlobalDCEPass()); + } + addExtensionsToPM(EP_EnabledOnOptLevel0, MPM); // Rename anon globals to be able to export them in the summary. Index: llvm/test/LTO/Resolution/X86/Inputs/dead-strip-fulllto.ll =================================================================== --- llvm/test/LTO/Resolution/X86/Inputs/dead-strip-fulllto.ll +++ llvm/test/LTO/Resolution/X86/Inputs/dead-strip-fulllto.ll @@ -14,3 +14,11 @@ } declare void @dead2() + +define linkonce_odr i8* @odr() { + ret i8* bitcast (void ()* @dead3 to i8*) +} + +define internal void @dead3() { + ret void +} Index: llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll =================================================================== --- llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll +++ llvm/test/LTO/Resolution/X86/dead-strip-fulllto.ll @@ -1,11 +1,18 @@ ; RUN: opt -module-summary -o %t %s ; RUN: opt -module-summary -o %t2 %S/Inputs/dead-strip-fulllto.ll + ; RUN: llvm-lto2 run %t -r %t,main,px -r %t,live1,p -r %t,live2,p -r %t,dead2,p \ -; RUN: %t2 -r %t2,live1,p -r %t2,live2, -r %t2,dead1,p -r %t2,dead2, \ +; RUN: %t2 -r %t2,live1,p -r %t2,live2, -r %t2,dead1,p -r %t2,dead2, -r %t2,odr, \ ; RUN: -save-temps -o %t3 ; RUN: llvm-nm %t3.0 | FileCheck --check-prefix=FULL %s ; RUN: llvm-nm %t3.1 | FileCheck --check-prefix=THIN %s +; RUN: llvm-lto2 run %t -r %t,main,px -r %t,live1,p -r %t,live2,p -r %t,dead2,p \ +; RUN: %t2 -r %t2,live1,p -r %t2,live2, -r %t2,dead1,p -r %t2,dead2, -r %t2,odr, \ +; RUN: -save-temps -o %t3 -O0 +; RUN: llvm-nm %t3.0 | FileCheck --check-prefix=FULL %s +; RUN: llvm-nm %t3.1 | FileCheck --check-prefix=THIN %s + ; FULL-NOT: dead ; FULL: U live1 ; FULL: T live2 @@ -14,6 +21,7 @@ ; THIN-NOT: dead ; THIN: T live1 ; THIN: U live2 +; THIN-NOT: odr target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-unknown-linux-gnu"