Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -1434,7 +1434,7 @@ // We don't import GV with references, because it can result // in promotion of local variables in the source module. return !GlobalValue::isInterposableLinkage(S->linkage()) && - !S->notEligibleToImport() && S->refs().empty(); + !S->notEligibleToImport(); } } // end namespace llvm Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -279,6 +279,11 @@ return Index.getValueInfo(GUID); } +static bool hasRefsPreventingImport(const GlobalValueSummary *S) { + auto *GVS = cast(S); + return !GVS->maybeReadOnly() && GVS->refs().size(); +}; + static void computeImportForReferencedGlobals( const FunctionSummary &Summary, const GVSummaryMapTy &DefinedGVSummaries, FunctionImporter::ImportMapTy &ImportList, @@ -303,16 +308,25 @@ RefSummary->modulePath() != Summary.modulePath(); }; + auto MarkExported = [&](const ValueInfo &VI, const GlobalValueSummary *S) { + if (ExportLists) + (*ExportLists)[S->modulePath()].insert(VI.getGUID()); + }; + for (auto &RefSummary : VI.getSummaryList()) if (isa(RefSummary.get()) && canImportGlobalVar(RefSummary.get()) && + !hasRefsPreventingImport(RefSummary.get()) && !LocalNotInModule(RefSummary.get())) { auto ILI = ImportList[RefSummary->modulePath()].insert(VI.getGUID()); // Only update stat if we haven't already imported this variable. if (ILI.second) NumImportedGlobalVarsThinLink++; - if (ExportLists) - (*ExportLists)[RefSummary->modulePath()].insert(VI.getGUID()); + MarkExported(VI, RefSummary.get()); + // Promote referenced functions and variables + for (const auto &VI : RefSummary->refs()) + for (const auto &RefFn : VI.getSummaryList()) + MarkExported(VI, RefFn.get()); break; } } Index: test/ThinLTO/X86/Inputs/indirect-call.ll =================================================================== --- test/ThinLTO/X86/Inputs/indirect-call.ll +++ test/ThinLTO/X86/Inputs/indirect-call.ll @@ -0,0 +1,12 @@ +source_filename = "foo.cpp" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.S = type { i32 ()*, i32* } + +@_ZL8localVar = internal global i32 11, align 4 +@Obj = dso_local local_unnamed_addr global %struct.S { i32 ()* @_ZL3barv, i32* @_ZL8localVar }, align 8 + +define internal i32 @_ZL3barv() { + ret i32 42 +} Index: test/ThinLTO/X86/indirect-call.ll =================================================================== --- test/ThinLTO/X86/indirect-call.ll +++ test/ThinLTO/X86/indirect-call.ll @@ -0,0 +1,29 @@ +; Checks that we replace indirect call to a function referenced +; in readonly structure initializer with a direct call. +; RUN: opt -thinlto-bc %s -o %t1 +; RUN: opt -thinlto-bc %p/Inputs/indirect-call.ll -o %t2 +; RUN: llvm-lto2 run -save-temps %t1 %t2 -o %t-out \ +; RUN: -r=%t2,Obj,pl \ +; RUN: -r=%t1,main,plx \ +; RUN: -r=%t1,Obj, +; RUN: llvm-dis %t-out.1.5.precodegen.bc -o - | FileCheck %s --check-prefix CODEGEN +; RUN: llvm-dis %t-out.2.1.promote.bc -o - | FileCheck %s --check-prefix PROMOTE + +; PROMOTE: @_ZL8localVar.llvm.{{[0-9]+}} = hidden global +; PROMOTE: define hidden i32 @_ZL3barv.llvm.{{[0-9]+}}() + +source_filename = "main.cpp" +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.S = type { i32 ()*, i32* } + +@Obj = external dso_local local_unnamed_addr global %struct.S, align 8 + +define dso_local i32 @main() local_unnamed_addr { + %1 = load i32 ()*, i32 ()** getelementptr inbounds (%struct.S, %struct.S* @Obj, i64 0, i32 0), align 8 +; CODEGEN: %[[VAR:.*]] = tail call i32 @_ZL3barv.llvm.{{[0-9]+}}() + %2 = tail call i32 %1() +; CODEGEN-NEXT ret i32 %[[VAR]] + ret i32 %2 +}