Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -213,6 +213,10 @@ FS_COMBINED_PROFILE = 5, // COMBINED_GLOBALVAR_INIT_REFS: [modid, linkage, n x valueid] FS_COMBINED_GLOBALVAR_INIT_REFS = 6, + // ALIAS: [valueid, linkage, valueid] + FS_ALIAS = 7, + // COMBINED_ALIAS: [modid, linkage, offset] + FS_COMBINED_ALIAS = 8, }; enum MetadataCodes { Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -92,7 +92,7 @@ class GlobalValueSummary { public: /// \brief Sububclass discriminator (for dyn_cast<> et al.) - enum SummaryKind { FunctionKind, GlobalVarKind }; + enum SummaryKind { AliasKind, FunctionKind, GlobalVarKind }; private: /// Kind of summary for use in dyn_cast<> et al. @@ -168,6 +168,32 @@ const std::vector &refs() const { return RefEdgeList; } }; +/// \brief Alias summary information. +class AliasSummary : public GlobalValueSummary { + GlobalValueSummary *AliaseeSummary; + +public: + /// Summary constructors. + AliasSummary(GlobalValue::LinkageTypes Linkage) + : GlobalValueSummary(AliasKind, Linkage) {} + + /// Check if this is an alias summary. + static bool classof(const GlobalValueSummary *GVS) { + return GVS->getSummaryKind() == AliasKind; + } + + void setAliasee(GlobalValueSummary *Aliasee) { AliaseeSummary = Aliasee; } + + const GlobalValueSummary &getAliasee() const { + return const_cast(this)->getAliasee(); + } + + GlobalValueSummary &getAliasee() { + assert(AliaseeSummary && "Unexpected missing aliasee summary"); + return *AliaseeSummary; + } +}; + /// \brief Function summary information to aid decisions and implementation of /// importing. class FunctionSummary : public GlobalValueSummary { Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -5844,6 +5844,35 @@ Info->setSummary(std::move(FS)); break; } + // FS_ALIAS: [valueid, linkage, valueid] + // Aliases must be emitted (and parsed) after all FS_PERMODULE entries, as + // they expect all aliasee summaries to be available. + case bitc::FS_ALIAS: { + unsigned ValueID = Record[0]; + uint64_t RawLinkage = Record[1]; + unsigned AliaseeID = Record[2]; + std::unique_ptr AS = + llvm::make_unique(getDecodedLinkage(RawLinkage)); + // The module path string ref set in the summary must be owned by the + // index's module string table. Since we don't have a module path + // string table section in the per-module index, we create a single + // module path string table entry with an empty (0) ID to take + // ownership. + AS->setModulePath( + TheIndex->addModulePath(Buffer->getBufferIdentifier(), 0)->first()); + + GlobalValue::GUID AliaseeGUID = getGUIDFromValueId(AliaseeID); + auto *AliaseeInfo = TheIndex->getGlobalValueInfo(AliaseeGUID); + if (!AliaseeInfo->summary()) + return error("Alias expects aliasee summary to be parsed"); + AS->setAliasee(AliaseeInfo->summary()); + + GlobalValue::GUID GUID = getGUIDFromValueId(ValueID); + auto *Info = TheIndex->getGlobalValueInfo(GUID); + assert(!Info->summary() && "Expected a single summary per VST entry"); + Info->setSummary(std::move(AS)); + break; + } // FS_PERMODULE_GLOBALVAR_INIT_REFS: [valueid, linkage, n x valueid] case bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS: { unsigned ValueID = Record[0]; @@ -5902,6 +5931,28 @@ Combined = true; break; } + // FS_COMBINED_ALIAS: [modid, linkage, offset] + // Aliases must be emitted (and parsed) after all FS_PERMODULE entries, as + // they expect all aliasee summaries to be available. + case bitc::FS_COMBINED_ALIAS: { + uint64_t ModuleId = Record[0]; + uint64_t RawLinkage = Record[1]; + uint64_t AliaseeSummaryOffset = Record[2]; + std::unique_ptr AS = + llvm::make_unique(getDecodedLinkage(RawLinkage)); + AS->setModulePath(ModuleIdMap[ModuleId]); + + auto *AliaseeInfo = getInfoFromSummaryOffset(AliaseeSummaryOffset); + if (!AliaseeInfo->summary()) + return error("Alias expects aliasee summary to be parsed"); + AS->setAliasee(AliaseeInfo->summary()); + + auto *Info = getInfoFromSummaryOffset(CurRecordBit); + assert(!Info->summary() && "Expected a single summary per VST entry"); + Info->setSummary(std::move(AS)); + Combined = true; + break; + } // FS_COMBINED_GLOBALVAR_INIT_REFS: [modid, linkage, n x valueid] case bitc::FS_COMBINED_GLOBALVAR_INIT_REFS: { uint64_t ModuleId = Record[0]; Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -2922,14 +2922,22 @@ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); unsigned FSModRefsAbbrev = Stream.EmitAbbrev(Abbv); + // Abbrev for FS_ALIAS. + Abbv = new BitCodeAbbrev(); + Abbv->Add(BitCodeAbbrevOp(bitc::FS_ALIAS)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 5)); // linkage + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid + unsigned FSAliasAbbrev = Stream.EmitAbbrev(Abbv); + SmallVector NameVals; // Iterate over the list of functions instead of the Index to // ensure the ordering is stable. for (const Function &F : *M) { if (F.isDeclaration()) continue; - // Skip anonymous functions. We will emit a function summary for - // any aliases below. + // We shouldn't have any anonymous functions as they are not supported in + // ThinLTO and should be renamed, but if any remains here, just skip them. if (!F.hasName()) continue; @@ -2940,20 +2948,6 @@ FSCallsAbbrev, FSCallsProfileAbbrev, Stream, F); } - for (const GlobalAlias &A : M->aliases()) { - if (!A.getBaseObject()) - continue; - const Function *F = dyn_cast(A.getBaseObject()); - if (!F || F->isDeclaration()) - continue; - - auto *Info = Index.getGlobalValueInfo(A); - WritePerModuleFunctionSummaryRecord( - NameVals, Info, - VE.getValueID(M->getValueSymbolTable().lookup(A.getName())), VE, - FSCallsAbbrev, FSCallsProfileAbbrev, Stream, *F); - } - // Capture references from GlobalVariable initializers, which are outside // of a function scope. for (const GlobalVariable &G : M->globals()) @@ -2963,6 +2957,20 @@ WriteModuleLevelReferences(*GV, Index, VE, NameVals, FSModRefsAbbrev, Stream); + for (const GlobalAlias &A : M->aliases()) { + auto *Aliasee = A.getBaseObject(); + if (!Aliasee->hasName()) + // Nameless function don't have an entry in the summary, skip it. + continue; + auto AliasId = VE.getValueID(&A); + auto AliaseeId = VE.getValueID(Aliasee); + NameVals.push_back(AliasId); + NameVals.push_back(getEncodedLinkage(A.getLinkage())); + NameVals.push_back(AliaseeId); + Stream.EmitRecord(bitc::FS_ALIAS, NameVals, FSAliasAbbrev); + NameVals.clear(); + } + Stream.ExitBlock(); } @@ -3006,11 +3014,31 @@ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); unsigned FSModRefsAbbrev = Stream.EmitAbbrev(Abbv); + // Abbrev for FS_COMBINED_ALIAS. + Abbv = new BitCodeAbbrev(); + Abbv->Add(BitCodeAbbrevOp(bitc::FS_COMBINED_ALIAS)); + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // modid + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 5)); // linkage + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // offset + unsigned FSAliasAbbrev = Stream.EmitAbbrev(Abbv); + + // The aliases are emitted as a post-pass, and will point to the summary + // offset id of the aliasee. For this purpose we need to be able to get back + // from the summary to the offset + SmallVector Aliases; + DenseMap SummaryToOffsetMap; + SmallVector NameVals; for (const auto &FII : Index) { for (auto &FI : FII.second) { GlobalValueSummary *S = FI->summary(); assert(S); + if (isa(S)) { + // Will process aliases as a post-pass because the reader wants all + // global to be loaded first. + Aliases.push_back(FI.get()); + continue; + } if (auto *VS = dyn_cast(S)) { NameVals.push_back(Index.getModuleId(VS->modulePath())); @@ -3033,6 +3061,8 @@ // reader will invoke readRecord after the abbrev id read. FI->setBitcodeIndex(Stream.GetCurrentBitNo() + Stream.GetAbbrevIDWidth()); + // Store temporarily the offset in the map for a possible alias. + SummaryToOffsetMap[S] = FI->bitcodeIndex(); // Emit the finished record. Stream.EmitRecord(bitc::FS_COMBINED_GLOBALVAR_INIT_REFS, NameVals, @@ -3084,6 +3114,8 @@ // in the VST entry. Add the current code size since the // reader will invoke readRecord after the abbrev id read. FI->setBitcodeIndex(Stream.GetCurrentBitNo() + Stream.GetAbbrevIDWidth()); + // Store temporarily the offset in the map for a possible alias. + SummaryToOffsetMap[S] = FI->bitcodeIndex(); unsigned FSAbbrev = (HasProfileData ? FSCallsProfileAbbrev : FSCallsAbbrev); @@ -3096,6 +3128,24 @@ } } + for (auto GVI : Aliases) { + AliasSummary *AS = cast(GVI->summary()); + NameVals.push_back(Index.getModuleId(AS->modulePath())); + NameVals.push_back(getEncodedLinkage(AS->linkage())); + auto AliaseeOffset = SummaryToOffsetMap[&AS->getAliasee()]; + assert(AliaseeOffset); + NameVals.push_back(AliaseeOffset); + + // Record the starting offset of this summary entry for use + // in the VST entry. Add the current code size since the + // reader will invoke readRecord after the abbrev id read. + GVI->setBitcodeIndex(Stream.GetCurrentBitNo() + Stream.GetAbbrevIDWidth()); + + // Emit the finished record. + Stream.EmitRecord(bitc::FS_COMBINED_ALIAS, NameVals, FSAliasAbbrev); + NameVals.clear(); + } + Stream.ExitBlock(); } Index: lib/Transforms/IPO/FunctionImport.cpp =================================================================== --- lib/Transforms/IPO/FunctionImport.cpp +++ lib/Transforms/IPO/FunctionImport.cpp @@ -79,13 +79,16 @@ /// number of source modules parsed/linked. /// - One that has PGO data attached. /// - [insert you fancy metric here] -static const FunctionSummary * +static const GlobalValueSummary * selectCallee(const GlobalValueInfoList &CalleeInfoList, unsigned Threshold) { auto It = llvm::find_if( CalleeInfoList, [&](const std::unique_ptr &GlobInfo) { assert(GlobInfo->summary() && "We should not have a Global Info without summary"); - auto *Summary = cast(GlobInfo->summary()); + auto *GVSummary = GlobInfo->summary(); + if (auto *AS = dyn_cast(GVSummary)) + GVSummary = &AS->getAliasee(); + auto *Summary = cast(GVSummary); if (GlobalValue::isWeakAnyLinkage(Summary->linkage())) return false; @@ -98,14 +101,14 @@ if (It == CalleeInfoList.end()) return nullptr; - return cast((*It)->summary()); + return cast((*It)->summary()); } /// Return the summary for the function \p GUID that fits the \p Threshold, or /// null if there's no match. -static const FunctionSummary *selectCallee(GlobalValue::GUID GUID, - unsigned Threshold, - const ModuleSummaryIndex &Index) { +static const GlobalValueSummary *selectCallee(GlobalValue::GUID GUID, + unsigned Threshold, + const ModuleSummaryIndex &Index) { auto CalleeInfoList = Index.findGlobalValueInfoList(GUID); if (CalleeInfoList == Index.end()) { return nullptr; // This function does not have a summary @@ -140,7 +143,7 @@ static void computeImportForFunction( StringRef ModulePath, const FunctionSummary &Summary, const ModuleSummaryIndex &Index, unsigned Threshold, - const std::map &DefinedFunctions, + const std::map &DefinedFunctions, SmallVectorImpl &Worklist, FunctionImporter::ImportMapTy &ImportsForModule, StringMap &ExportLists) { @@ -158,11 +161,19 @@ DEBUG(dbgs() << "ignored! No qualifying callee with summary found.\n"); continue; } - assert(CalleeSummary->instCount() <= Threshold && + // "Resolve" the summary, traversing alias, + const FunctionSummary *ResolvedCalleeSummary; + if (isa(CalleeSummary)) + ResolvedCalleeSummary = cast( + &cast(CalleeSummary)->getAliasee()); + else + ResolvedCalleeSummary = cast(CalleeSummary); + + assert(ResolvedCalleeSummary->instCount() <= Threshold && "selectCallee() didn't honor the threshold"); auto &ProcessedThreshold = - ImportsForModule[CalleeSummary->modulePath()][GUID]; + ImportsForModule[ResolvedCalleeSummary->modulePath()][GUID]; /// Since the traversal of the call graph is DFS, we can revisit a function /// a second time with a higher threshold. In this case, it is added back to /// the worklist with the new threshold. @@ -175,24 +186,24 @@ ProcessedThreshold = Threshold; // Make exports in the source module. - auto ExportModulePath = CalleeSummary->modulePath(); + auto ExportModulePath = ResolvedCalleeSummary->modulePath(); auto ExportList = ExportLists[ExportModulePath]; ExportList.insert(GUID); // Mark all functions and globals referenced by this function as exported to // the outside if they are defined in the same source module. - for (auto &Edge : CalleeSummary->calls()) { + for (auto &Edge : ResolvedCalleeSummary->calls()) { auto CalleeGUID = Edge.first.getId(); if (isGlobalExported(Index, ExportModulePath, CalleeGUID)) ExportList.insert(CalleeGUID); } - for (auto &Ref : CalleeSummary->refs()) { + for (auto &Ref : ResolvedCalleeSummary->refs()) { auto GUID = Ref.getId(); if (isGlobalExported(Index, ExportModulePath, GUID)) ExportList.insert(GUID); } // Insert the newly imported function to the worklist. - Worklist.push_back(std::make_pair(CalleeSummary, Threshold)); + Worklist.push_back(std::make_pair(ResolvedCalleeSummary, Threshold)); } } @@ -201,7 +212,7 @@ /// another module (that may require promotion). static void ComputeImportForModule( StringRef ModulePath, - const std::map &DefinedFunctions, + const std::map &DefinedFunctions, const ModuleSummaryIndex &Index, FunctionImporter::ImportMapTy &ImportsForModule, StringMap &ExportLists) { @@ -213,8 +224,11 @@ // module for (auto &FuncInfo : DefinedFunctions) { auto *Summary = FuncInfo.second; + if (auto *AS = dyn_cast(Summary)) + Summary = &AS->getAliasee(); + auto *FuncSummary = cast(Summary); DEBUG(dbgs() << "Initalize import for " << FuncInfo.first << "\n"); - computeImportForFunction(ModulePath, *Summary, Index, ImportInstrLimit, + computeImportForFunction(ModulePath, *FuncSummary, Index, ImportInstrLimit, DefinedFunctions, Worklist, ImportsForModule, ExportLists); } @@ -245,16 +259,20 @@ // Collect for each module the list of function it defines. // GUID -> Summary - StringMap> + StringMap> Module2FunctionInfoMap(ModuleCount); for (auto &GlobalList : Index) { auto GUID = GlobalList.first; for (auto &GlobInfo : GlobalList.second) { - auto *Summary = dyn_cast_or_null(GlobInfo->summary()); - if (!Summary) + auto *Summary = GlobInfo->summary(); + if (isa(Summary)) /// Ignore global variable, focus on functions continue; + if (auto *AS = dyn_cast(Summary)) + if (isa(&AS->getAliasee())) + /// Ignore alias to global variable, focus on functions + continue; DEBUG(dbgs() << "Adding definition: Module '" << Summary->modulePath() << "' defines '" << GUID << "'\n"); Module2FunctionInfoMap[Summary->modulePath()][GUID] = Summary; @@ -333,7 +351,7 @@ GlobalsToImport.insert(&GV); } } - for (auto &GV : SrcModule->aliases()) { + for (auto &GV : SrcModule->globals()) { if (!GV.hasName()) continue; auto GUID = GV.getGUID(); @@ -342,18 +360,11 @@ << GV.getName() << " from " << SrcModule->getSourceFileName() << "\n"); if (Import) { - // Alias can't point to "available_externally". However when we import - // linkOnceODR the linkage does not change. So we import the alias - // and aliasee only in this case. - const GlobalObject *GO = GV.getBaseObject(); - if (!GO->hasLinkOnceODRLinkage()) - continue; GV.materialize(); GlobalsToImport.insert(&GV); - GlobalsToImport.insert(GO); } } - for (auto &GV : SrcModule->globals()) { + for (auto &GV : SrcModule->aliases()) { if (!GV.hasName()) continue; auto GUID = GV.getGUID(); @@ -362,6 +373,20 @@ << GV.getName() << " from " << SrcModule->getSourceFileName() << "\n"); if (Import) { + // Alias can't point to "available_externally". However when we import + // linkOnceODR the linkage does not change. So we import the alias + // and aliasee only in this case. + GlobalObject *GO = GV.getBaseObject(); + if (!GO->hasLinkOnceODRLinkage()) + continue; +#ifndef NDEBUG + if (!GlobalsToImport.count(GO)) + DEBUG(dbgs() << " alias triggers importing aliasee " << GO->getGUID() + << " " << GO->getName() << " from " + << SrcModule->getSourceFileName() << "\n"); +#endif + GO->materialize(); + GlobalsToImport.insert(GO); GV.materialize(); GlobalsToImport.insert(&GV); } @@ -439,9 +464,7 @@ static char ID; /// Specify pass name for debug output - const char *getPassName() const override { - return "Function Importing"; - } + const char *getPassName() const override { return "Function Importing"; } explicit FunctionImportPass(const ModuleSummaryIndex *Index = nullptr) : ModulePass(ID), Index(Index) {} Index: test/Bitcode/Inputs/thinlto-alias.ll =================================================================== --- /dev/null +++ test/Bitcode/Inputs/thinlto-alias.ll @@ -0,0 +1,13 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + + + +@analias = alias void (...), bitcast (void ()* @aliasee to void (...)*) + +; Function Attrs: nounwind uwtable +define void @aliasee() #0 { +entry: + ret void +} + Index: test/Bitcode/thinlto-alias.ll =================================================================== --- /dev/null +++ test/Bitcode/thinlto-alias.ll @@ -0,0 +1,45 @@ +; Test to check the callgraph in summary +; RUN: llvm-as -module-summary %s -o %t.o +; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s +; RUN: llvm-as -module-summary %p/Inputs/thinlto-alias.ll -o %t2.o +; RUN: llvm-lto -thinlto -o %t3 %t.o %t2.o +; RUN: llvm-bcanalyzer -dump %t3.thinlto.bc | FileCheck %s --check-prefix=COMBINED + +; CHECK: +; CHECK-NEXT: +; CHECK-NEXT: + +; COMBINED: +; Followed by the alias and aliasee +; COMBINED-NEXT: +; COMBINED-NEXT: +; COMBINED-NEXT: + +; ModuleID = 'thinlto-function-summary-callgraph.ll' +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: nounwind uwtable +define i32 @main() { +entry: + call void (...) @analias() + ret i32 0 +} + +declare void @analias(...) Index: test/Bitcode/thinlto-function-summary.ll =================================================================== --- test/Bitcode/thinlto-function-summary.ll +++ test/Bitcode/thinlto-function-summary.ll @@ -7,7 +7,6 @@ ; BC: record string = 'foo' Index: tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp =================================================================== --- tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -308,6 +308,8 @@ STRINGIFY_CODE(FS, COMBINED) STRINGIFY_CODE(FS, COMBINED_PROFILE) STRINGIFY_CODE(FS, COMBINED_GLOBALVAR_INIT_REFS) + STRINGIFY_CODE(FS, ALIAS) + STRINGIFY_CODE(FS, COMBINED_ALIAS) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) {