Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -312,7 +312,7 @@ StringRef modulePath() const { return ModulePath; } /// Get the flags for this GlobalValue (see \p struct GVFlags). - GVFlags flags() { return Flags; } + GVFlags flags() const { return Flags; } /// Return linkage type recorded for this global value. GlobalValue::LinkageTypes linkage() const { @@ -428,6 +428,25 @@ std::vector Args; }; + /// All type identifier related information. Because these fields are + /// relatively uncommon we only allocate space for them if necessary. + struct TypeIdInfo { + /// List of type identifiers used by this function in llvm.type.test + /// intrinsics other than by an llvm.assume intrinsic, represented as GUIDs. + std::vector TypeTests; + + /// List of virtual calls made by this function using (respectively) + /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics that do + /// not have all constant integer arguments. + std::vector TypeTestAssumeVCalls, TypeCheckedLoadVCalls; + + /// List of virtual calls made by this function using (respectively) + /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics with + /// all constant integer arguments. + std::vector TypeTestAssumeConstVCalls, + TypeCheckedLoadConstVCalls; + }; + /// Function attribute flags. Used to track if a function accesses memory, /// recurses or aliases. struct FFlags { @@ -468,25 +487,6 @@ /// List of call edge pairs from this function. std::vector CallGraphEdgeList; - /// All type identifier related information. Because these fields are - /// relatively uncommon we only allocate space for them if necessary. - struct TypeIdInfo { - /// List of type identifiers used by this function in llvm.type.test - /// intrinsics other than by an llvm.assume intrinsic, represented as GUIDs. - std::vector TypeTests; - - /// List of virtual calls made by this function using (respectively) - /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics that do - /// not have all constant integer arguments. - std::vector TypeTestAssumeVCalls, TypeCheckedLoadVCalls; - - /// List of virtual calls made by this function using (respectively) - /// llvm.assume(llvm.type.test) or llvm.type.checked.load intrinsics with - /// all constant integer arguments. - std::vector TypeTestAssumeConstVCalls, - TypeCheckedLoadConstVCalls; - }; - std::unique_ptr TIdInfo; public: @@ -516,7 +516,7 @@ } /// Get function attribute flags. - FFlags &fflags() { return FunFlags; } + FFlags fflags() const { return FunFlags; } /// Get the instruction count recorded for this function. unsigned instCount() const { return InstCount; } @@ -576,6 +576,8 @@ TIdInfo->TypeTests.push_back(Guid); } + const TypeIdInfo *getTypeIdInfo() const { return TIdInfo.get(); }; + friend struct GraphTraits; }; @@ -864,6 +866,12 @@ } bool isGUIDLive(GlobalValue::GUID GUID) const; + /// Return a ValueInfo for the index value_type (convenient when iterating + /// index). + ValueInfo getValueInfo(const GlobalValueSummaryMapTy::value_type *R) const { + return ValueInfo(IsAnalysis, R); + } + /// Return a ValueInfo for GUID if it exists, otherwise return ValueInfo(). ValueInfo getValueInfo(GlobalValue::GUID GUID) const { auto I = GlobalValueMap.find(GUID); @@ -1048,6 +1056,12 @@ void collectDefinedGVSummariesPerModule( StringMap &ModuleToDefinedGVSummaries) const; + /// Print to an output stream. + void print(raw_ostream &OS, bool IsForDebug = false) const; + + /// Dump to stderr (for debugging). + void dump() const; + /// Export summary to dot file for GraphViz. void exportToDot(raw_ostream& OS) const; Index: lib/AsmParser/LLLexer.h =================================================================== --- lib/AsmParser/LLLexer.h +++ lib/AsmParser/LLLexer.h @@ -65,12 +65,13 @@ void Warning(LocTy WarningLoc, const Twine &Msg) const; void Warning(const Twine &Msg) const { return Warning(getLoc(), Msg); } + // FIXME: Make private again when summary parsing support is complete. + void SkipLineComment(); private: lltok::Kind LexToken(); int getNextChar(); - void SkipLineComment(); lltok::Kind ReadString(lltok::Kind kind); bool ReadVarName(); @@ -85,6 +86,7 @@ lltok::Kind LexQuote(); lltok::Kind Lex0x(); lltok::Kind LexHash(); + lltok::Kind LexCaret(); uint64_t atoull(const char *Buffer, const char *End); uint64_t HexIntToVal(const char *Buffer, const char *End); Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -219,6 +219,8 @@ SkipLineComment(); continue; case '!': return LexExclaim(); + case '^': + return LexCaret(); case '#': return LexHash(); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -420,6 +422,24 @@ return lltok::exclaim; } +/// Lex all tokens that start with a ^ character. +/// SummaryID ::= ^[0-9]+ +lltok::Kind LLLexer::LexCaret() { + // Handle SummaryID: ^[0-9]+ + if (isdigit(static_cast(CurPtr[0]))) { + for (++CurPtr; isdigit(static_cast(CurPtr[0])); ++CurPtr) + /*empty*/; + + uint64_t Val = atoull(TokStart + 1, CurPtr); + if ((unsigned)Val != Val) + Error("invalid value number (too large)!"); + UIntVal = unsigned(Val); + return lltok::SummaryID; + } + + return lltok::Error; +} + /// Lex all tokens that start with a # character. /// AttrGrpID ::= #[0-9]+ lltok::Kind LLLexer::LexHash() { Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -312,6 +312,7 @@ bool ParseFnAttributeValuePairs(AttrBuilder &B, std::vector &FwdRefAttrGrps, bool inAttrGrp, LocTy &BuiltinLoc); + bool ParseSummaryEntry(); // Type Parsing. bool ParseType(Type *&Result, const Twine &Msg, bool AllowVoid = false); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -282,6 +282,10 @@ case lltok::GlobalVar: if (ParseNamedGlobal()) return true; break; case lltok::ComdatVar: if (parseComdat()) return true; break; case lltok::exclaim: if (ParseStandaloneMetadata()) return true; break; + case lltok::SummaryID: + if (ParseSummaryEntry()) + return true; + break; case lltok::MetadataVar:if (ParseNamedMetadata()) return true; break; case lltok::kw_attributes: if (ParseUnnamedAttrGrp()) return true; break; case lltok::kw_uselistorder: if (ParseUseListOrder()) return true; break; @@ -711,6 +715,23 @@ return false; } +/// ParseSummaryEntry: +/// ::= SummaryID '=' ... +bool LLParser::ParseSummaryEntry() { + assert(Lex.getKind() == lltok::SummaryID); + // unsigned SummaryID = Lex.getUIntVal(); + + Lex.Lex(); + if (ParseToken(lltok::equal, "expected '=' here")) + return true; + + // TODO: Support parsing into a ModuleSummaryIndex object saved in + // the LLParser. For now, skip the summary entry. + Lex.SkipLineComment(); + Lex.Lex(); + return false; +} + static bool isValidVisibilityForLinkage(unsigned V, unsigned L) { return !GlobalValue::isLocalLinkage((GlobalValue::LinkageTypes)L) || (GlobalValue::VisibilityTypes)V == GlobalValue::DefaultVisibility; Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -351,6 +351,7 @@ GlobalID, // @42 LocalVarID, // %42 AttrGrpID, // #42 + SummaryID, // ^42 // String valued tokens (StrVal). LabelStr, // foo: Index: lib/IR/AsmWriter.cpp =================================================================== --- lib/IR/AsmWriter.cpp +++ lib/IR/AsmWriter.cpp @@ -56,6 +56,7 @@ #include "llvm/IR/Metadata.h" #include "llvm/IR/Module.h" #include "llvm/IR/ModuleSlotTracker.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Statepoint.h" #include "llvm/IR/Type.h" @@ -673,6 +674,9 @@ bool FunctionProcessed = false; bool ShouldInitializeAllMetadata; + /// The summary index for which we are holding slot numbers. + const ModuleSummaryIndex *TheIndex = nullptr; + /// mMap - The slot map for the module level data. ValueMap mMap; unsigned mNext = 0; @@ -689,6 +693,10 @@ DenseMap asMap; unsigned asNext = 0; + /// GUIDMap - The slot map for GUIDs used in the summary index. + DenseMap GUIDMap; + unsigned GUIDNext = 0; + public: /// Construct from a module. /// @@ -706,6 +714,9 @@ explicit SlotTracker(const Function *F, bool ShouldInitializeAllMetadata = false); + /// Construct from a module summary index. + explicit SlotTracker(const ModuleSummaryIndex *Index); + SlotTracker(const SlotTracker &) = delete; SlotTracker &operator=(const SlotTracker &) = delete; @@ -715,6 +726,7 @@ int getGlobalSlot(const GlobalValue *V); int getMetadataSlot(const MDNode *N); int getAttributeGroupSlot(AttributeSet AS); + int getGUIDSlot(GlobalValue::GUID GUID); /// If you'd like to deal with a function instead of just a module, use /// this method to get its data into the SlotTracker. @@ -746,8 +758,15 @@ unsigned as_size() const { return asMap.size(); } bool as_empty() const { return asMap.empty(); } - /// This function does the actual initialization. + /// GUID map iterators. + using GUID_iterator = DenseMap::iterator; + + GUID_iterator GUID_begin() { return GUIDMap.begin(); } + GUID_iterator GUID_end() { return GUIDMap.end(); } + + /// These functions do the actual initialization. inline void initialize(); + inline void initializeIndex(); // Implementation Details private: @@ -763,9 +782,12 @@ /// \brief Insert the specified AttributeSet into the slot table. void CreateAttributeSetSlot(AttributeSet AS); + void CreateGUIDSlot(GlobalValue::GUID GUID); + /// Add all of the module level global variables (and their initializers) /// and function declarations, but not the contents of those functions. void processModule(); + void processIndex(); /// Add all of the functions arguments, basic blocks, and instructions. void processFunction(); @@ -866,6 +888,9 @@ : TheModule(F ? F->getParent() : nullptr), TheFunction(F), ShouldInitializeAllMetadata(ShouldInitializeAllMetadata) {} +SlotTracker::SlotTracker(const ModuleSummaryIndex *Index) + : TheModule(nullptr), ShouldInitializeAllMetadata(false), TheIndex(Index) {} + inline void SlotTracker::initialize() { if (TheModule) { processModule(); @@ -876,6 +901,13 @@ processFunction(); } +inline void SlotTracker::initializeIndex() { + if (TheIndex) { + processIndex(); + TheIndex = nullptr; ///< Prevent re-processing next time we're called. + } +} + // Iterate through all the global variables, functions, and global // variable initializers and create slots for them. void SlotTracker::processModule() { @@ -967,6 +999,23 @@ ST_DEBUG("end processFunction!\n"); } +// Iterate through all the GUID in the index and create slots for them. +void SlotTracker::processIndex() { + ST_DEBUG("begin processIndex!\n"); + + // The first block of slots are just the module ids, which start at 0 and are + // assigned consecutively. Start numbering the GUIDs after the module ids. + GUIDNext = TheIndex->modulePaths().size(); + + for (auto &GlobalList : *TheIndex) + CreateGUIDSlot(GlobalList.first); + + for (auto &TId : TheIndex->typeIds()) + CreateGUIDSlot(GlobalValue::getGUID(TId.first)); + + ST_DEBUG("end processIndex!\n"); +} + void SlotTracker::processGlobalObjectMetadata(const GlobalObject &GO) { SmallVector, 4> MDs; GO.getAllMetadata(MDs); @@ -1050,6 +1099,15 @@ return AI == asMap.end() ? -1 : (int)AI->second; } +int SlotTracker::getGUIDSlot(GlobalValue::GUID GUID) { + // Check for uninitialized state and do lazy initialization. + initializeIndex(); + + // Find the GUID in the map + GUID_iterator I = GUIDMap.find(GUID); + return I == GUIDMap.end() ? -1 : (int)I->second; +} + /// CreateModuleSlot - Insert the specified GlobalValue* into the slot table. void SlotTracker::CreateModuleSlot(const GlobalValue *V) { assert(V && "Can't insert a null Value into SlotTracker!"); @@ -1110,6 +1168,12 @@ asMap[AS] = DestSlot; } +/// Create a new slot for the specified GUID +void SlotTracker::CreateGUIDSlot(GlobalValue::GUID GUID) { + unsigned DestSlot = GUIDNext++; + GUIDMap[GUID] = DestSlot; +} + //===----------------------------------------------------------------------===// // AsmWriter Implementation //===----------------------------------------------------------------------===// @@ -2179,6 +2243,7 @@ class AssemblyWriter { formatted_raw_ostream &Out; const Module *TheModule; + const ModuleSummaryIndex *TheIndex; std::unique_ptr SlotTrackerStorage; SlotTracker &Machine; TypePrinting TypePrinter; @@ -2190,6 +2255,7 @@ SmallVector MDNames; /// Synchronization scope names registered with LLVMContext. SmallVector SSNs; + DenseMap SummaryToGUIDMap; public: /// Construct an AssemblyWriter with an external SlotTracker @@ -2197,6 +2263,9 @@ AssemblyAnnotationWriter *AAW, bool IsForDebug, bool ShouldPreserveUseListOrder = false); + AssemblyWriter(formatted_raw_ostream &o, SlotTracker &Mac, + const ModuleSummaryIndex *Index, bool IsForDebug); + void printMDNodeBody(const MDNode *MD); void printNamedMDNode(const NamedMDNode *NMD); @@ -2232,6 +2301,25 @@ void printUseListOrder(const UseListOrder &Order); void printUseLists(const Function *F); + void printModuleSummaryIndex(); + void printSummaryInfo(unsigned Slot, const ValueInfo &VI); + void printSummary(const GlobalValueSummary &Summary); + void printAliasSummary(const AliasSummary *AS); + void printGlobalVarSummary(const GlobalVarSummary *GS); + void printFunctionSummary(const FunctionSummary *FS); + void printTypeIdSummary(const TypeIdSummary &TIS); + void printTypeTestResolution(const TypeTestResolution &TTRes); + void printArgs(const std::vector &Args); + void printWPDRes(const WholeProgramDevirtResolution &WPDRes); + void printTypeIdInfo(const FunctionSummary::TypeIdInfo &TIDInfo); + void printVFuncId(const FunctionSummary::VFuncId VFId); + void + printNonConstVCalls(const std::vector VCallList, + const char *Tag); + void + printConstVCalls(const std::vector VCallList, + const char *Tag); + private: /// \brief Print out metadata attachments. void printMetadataAttachments( @@ -2252,8 +2340,8 @@ AssemblyWriter::AssemblyWriter(formatted_raw_ostream &o, SlotTracker &Mac, const Module *M, AssemblyAnnotationWriter *AAW, bool IsForDebug, bool ShouldPreserveUseListOrder) - : Out(o), TheModule(M), Machine(Mac), TypePrinter(M), AnnotationWriter(AAW), - IsForDebug(IsForDebug), + : Out(o), TheModule(M), TheIndex(nullptr), Machine(Mac), TypePrinter(M), + AnnotationWriter(AAW), IsForDebug(IsForDebug), ShouldPreserveUseListOrder(ShouldPreserveUseListOrder) { if (!TheModule) return; @@ -2262,6 +2350,12 @@ Comdats.insert(C); } +AssemblyWriter::AssemblyWriter(formatted_raw_ostream &o, SlotTracker &Mac, + const ModuleSummaryIndex *Index, bool IsForDebug) + : Out(o), TheModule(nullptr), TheIndex(Index), Machine(Mac), + TypePrinter(nullptr), AnnotationWriter(nullptr), IsForDebug(IsForDebug), + ShouldPreserveUseListOrder(false) {} + void AssemblyWriter::writeOperand(const Value *Operand, bool PrintType) { if (!Operand) { Out << ""; @@ -2463,6 +2557,415 @@ } } +void AssemblyWriter::printModuleSummaryIndex() { + assert(TheIndex); + Machine.initializeIndex(); + + Out << "\n"; + + // First print module path entries, using the module id as the slot number. To + // print in order, add paths to a vector indexed by module id. + std::vector> moduleVec; + std::string RegularLTOModuleName = "[Regular LTO]"; + moduleVec.resize(TheIndex->modulePaths().size()); + for (auto &ModPath : TheIndex->modulePaths()) { + // A module id of -1 is a special entry for a regular LTO module created + // during the thin link. + if (ModPath.second.first == -1u) + moduleVec[TheIndex->modulePaths().size() - 1] = + std::make_pair(RegularLTOModuleName, ModPath.second.second); + else { + assert(ModPath.second.first < moduleVec.size()); + moduleVec[ModPath.second.first] = + std::make_pair(ModPath.first(), ModPath.second.second); + } + } + unsigned i = 0; + for (auto &ModPair : moduleVec) { + Out << "^" << i++ << " = module: ("; + Out << "path: " << ModPair.first; + Out << ", hash: " + << (uint64_t(ModPair.second[0]) << 32 | ModPair.second[1]); + Out << ")\n"; + } + + // FIXME: Change AliasSummary to hold a ValueInfo instead of summary pointer + // for aliasee (then update BitcodeWriter.cpp and remove get/setAliaseeGUID). + for (auto &GlobalList : *TheIndex) { + auto GUID = GlobalList.first; + for (auto &Summary : GlobalList.second.SummaryList) + SummaryToGUIDMap[Summary.get()] = GUID; + } + + // Next print the global value summary entries. + for (auto &GlobalList : *TheIndex) { + auto GUID = GlobalList.first; + auto VI = TheIndex->getValueInfo(&GlobalList); + printSummaryInfo(Machine.getGUIDSlot(GUID), VI); + } + + // Last print the TypeIdMap entries. + for (auto &TId : TheIndex->typeIds()) { + auto GUID = GlobalValue::getGUID(TId.first); + Out << "^" << Machine.getGUIDSlot(GUID) + << " = typeid: (name: " << TId.first; + printTypeIdSummary(TId.second); + Out << ") ; guid = " << GUID << "\n"; + } +} + +static const char * +getWholeProgDevirtResKindName(WholeProgramDevirtResolution::Kind K) { + switch (K) { + case WholeProgramDevirtResolution::Indir: + return "indir"; + case WholeProgramDevirtResolution::SingleImpl: + return "singleImpl"; + case WholeProgramDevirtResolution::BranchFunnel: + return "branchFunnel"; + } + llvm_unreachable("invalid WholeProgramDevirtResolution kind"); +} + +static const char *getWholeProgDevirtResByArgKindName( + WholeProgramDevirtResolution::ByArg::Kind K) { + switch (K) { + case WholeProgramDevirtResolution::ByArg::Indir: + return "indir"; + case WholeProgramDevirtResolution::ByArg::UniformRetVal: + return "uniformRetVal"; + case WholeProgramDevirtResolution::ByArg::UniqueRetVal: + return "uniqueRetVal"; + case WholeProgramDevirtResolution::ByArg::VirtualConstProp: + return "virtualConstProp"; + } + llvm_unreachable("invalid WholeProgramDevirtResolution::ByArg kind"); +} + +static const char *getTTResKindName(TypeTestResolution::Kind K) { + switch (K) { + case TypeTestResolution::Unsat: + return "unsat"; + case TypeTestResolution::ByteArray: + return "byteArray"; + case TypeTestResolution::Inline: + return "inline"; + case TypeTestResolution::Single: + return "single"; + case TypeTestResolution::AllOnes: + return "allOnes"; + } + llvm_unreachable("invalid TypeTestResolution kind"); +} + +void AssemblyWriter::printTypeTestResolution(const TypeTestResolution &TTRes) { + Out << "typeTestRes: (kind: " << getTTResKindName(TTRes.TheKind) + << ", sizeM1BitWidth: " << TTRes.SizeM1BitWidth; + + // The following fields are only used if the target does not support the use + // of absolute symbols to store constants. Print only if non-zero. + if (TTRes.AlignLog2 || TTRes.SizeM1 || TTRes.BitMask || TTRes.InlineBits) { + Out << ", alignLog2: " << TTRes.AlignLog2; + Out << ", sizeM1: " << TTRes.SizeM1; + // BitMask is uint8_t which causes it to print the corresponding char. + Out << ", bitMask: " << (unsigned)TTRes.BitMask; + Out << ", inlineBits: " << TTRes.InlineBits; + } + + Out << ")"; +} + +void AssemblyWriter::printTypeIdSummary(const TypeIdSummary &TIS) { + Out << ", summary: ("; + printTypeTestResolution(TIS.TTRes); + if (TIS.WPDRes.size()) { + Out << ", wpdResolutions: ("; + FieldSeparator FS; + for (auto &WPDRes : TIS.WPDRes) { + Out << FS; + Out << "(offset: " << WPDRes.first << ", "; + printWPDRes(WPDRes.second); + Out << ")"; + } + Out << ")"; + } + Out << ")"; +} + +void AssemblyWriter::printArgs(const std::vector &Args) { + Out << "args: ("; + FieldSeparator FS; + for (auto arg : Args) { + Out << FS; + Out << arg; + } + Out << ")"; +} + +void AssemblyWriter::printWPDRes(const WholeProgramDevirtResolution &WPDRes) { + Out << "wpdRes: (kind: "; + Out << getWholeProgDevirtResKindName(WPDRes.TheKind); + + if (WPDRes.TheKind == WholeProgramDevirtResolution::SingleImpl) + Out << ", singleImplName: " << WPDRes.SingleImplName; + + if (WPDRes.ResByArg.size()) { + Out << ", resByArg: ("; + FieldSeparator FS; + for (auto &ResByArg : WPDRes.ResByArg) { + Out << FS; + printArgs(ResByArg.first); + Out << ", byArg: (kind: "; + Out << getWholeProgDevirtResByArgKindName(ResByArg.second.TheKind); + if (ResByArg.second.TheKind == + WholeProgramDevirtResolution::ByArg::UniformRetVal || + ResByArg.second.TheKind == + WholeProgramDevirtResolution::ByArg::UniqueRetVal) + Out << ", info: " << ResByArg.second.Info; + + // The following fields are only used if the target does not support the + // use of absolute symbols to store constants. Print only if non-zero. + if (ResByArg.second.Byte || ResByArg.second.Bit) + Out << ", byte: " << ResByArg.second.Byte + << ", bit: " << ResByArg.second.Bit; + + Out << ")"; + } + Out << ")"; + } + Out << ")"; +} + +static const char *getSummaryKindName(GlobalValueSummary::SummaryKind SK) { + switch (SK) { + case GlobalValueSummary::AliasKind: + return "alias"; + case GlobalValueSummary::FunctionKind: + return "function"; + case GlobalValueSummary::GlobalVarKind: + return "variable"; + } + llvm_unreachable("invalid summary kind"); +} + +void AssemblyWriter::printAliasSummary(const AliasSummary *AS) { + Out << ", aliasee: ^" + << Machine.getGUIDSlot(SummaryToGUIDMap[&AS->getAliasee()]); +} + +void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) { + // Nothing for now +} + +static std::string getLinkagePrintName(GlobalValue::LinkageTypes LT, + bool AddSpace = true) { + std::string Space = AddSpace ? " " : ""; + switch (LT) { + case GlobalValue::ExternalLinkage: + return ""; + case GlobalValue::PrivateLinkage: + return "private" + Space; + case GlobalValue::InternalLinkage: + return "internal" + Space; + case GlobalValue::LinkOnceAnyLinkage: + return "linkonce" + Space; + case GlobalValue::LinkOnceODRLinkage: + return "linkonce_odr" + Space; + case GlobalValue::WeakAnyLinkage: + return "weak" + Space; + case GlobalValue::WeakODRLinkage: + return "weak_odr" + Space; + case GlobalValue::CommonLinkage: + return "common" + Space; + case GlobalValue::AppendingLinkage: + return "appending" + Space; + case GlobalValue::ExternalWeakLinkage: + return "extern_weak" + Space; + case GlobalValue::AvailableExternallyLinkage: + return "available_externally" + Space; + } + llvm_unreachable("invalid linkage"); +} + +static const char *getHotnessName(CalleeInfo::HotnessType HT) { + switch (HT) { + case CalleeInfo::HotnessType::Unknown: + return "unknown"; + case CalleeInfo::HotnessType::Cold: + return "cold"; + case CalleeInfo::HotnessType::None: + return "none"; + case CalleeInfo::HotnessType::Hot: + return "hot"; + case CalleeInfo::HotnessType::Critical: + return "critical"; + } + llvm_unreachable("invalid hotness"); +} + +void AssemblyWriter::printFunctionSummary(const FunctionSummary *FS) { + Out << ", insts: " << FS->instCount(); + + FunctionSummary::FFlags FFlags = FS->fflags(); + if (FFlags.ReadNone | FFlags.ReadOnly | FFlags.NoRecurse | + FFlags.ReturnDoesNotAlias) { + Out << ", funcFlags: ("; + Out << "readNone: " << FFlags.ReadNone; + Out << ", readOnly: " << FFlags.ReadOnly; + Out << ", noRecurse: " << FFlags.NoRecurse; + Out << ", returnDoesNotAlias: " << FFlags.ReturnDoesNotAlias; + Out << ")"; + } + if (FS->calls().size() > 0) { + Out << ", calls: ("; + FieldSeparator IFS; + for (auto &Call : FS->calls()) { + Out << IFS; + Out << "(callee: ^" << Machine.getGUIDSlot(Call.first.getGUID()) + << ", hotness: " << getHotnessName(Call.second.getHotness()); + if (Call.second.RelBlockFreq) + Out << ", relbf: " << Call.second.RelBlockFreq; + Out << ")"; + } + Out << ")"; + } + + if (const auto *TIdInfo = FS->getTypeIdInfo()) + printTypeIdInfo(*TIdInfo); +} + +void AssemblyWriter::printTypeIdInfo( + const FunctionSummary::TypeIdInfo &TIDInfo) { + Out << ", typeIdInfo: ("; + FieldSeparator TIDFS; + if (TIDInfo.TypeTests.size()) { + Out << TIDFS; + Out << "typeTests: ("; + FieldSeparator FS; + for (auto &GUID : TIDInfo.TypeTests) { + Out << FS; + auto Slot = Machine.getGUIDSlot(GUID); + if (Slot != -1) + Out << "^" << Slot; + else + Out << GUID; + } + Out << ")"; + } + if (TIDInfo.TypeTestAssumeVCalls.size()) { + Out << TIDFS; + printNonConstVCalls(TIDInfo.TypeTestAssumeVCalls, "typeTestAssumeVCalls"); + } + if (TIDInfo.TypeCheckedLoadVCalls.size()) { + Out << TIDFS; + printNonConstVCalls(TIDInfo.TypeCheckedLoadVCalls, "typeCheckedLoadVCalls"); + } + if (TIDInfo.TypeTestAssumeConstVCalls.size()) { + Out << TIDFS; + printConstVCalls(TIDInfo.TypeTestAssumeConstVCalls, + "typeTestAssumeConstVCalls"); + } + if (TIDInfo.TypeCheckedLoadConstVCalls.size()) { + Out << TIDFS; + printConstVCalls(TIDInfo.TypeCheckedLoadConstVCalls, + "typeCheckedLoadConstVCalls"); + } + Out << ")"; +} + +void AssemblyWriter::printVFuncId(const FunctionSummary::VFuncId VFId) { + Out << "vFuncId: ("; + auto Slot = Machine.getGUIDSlot(VFId.GUID); + if (Slot != -1) + Out << "^" << Slot; + else + Out << "guid: " << VFId.GUID; + Out << ", offset: " << VFId.Offset; + Out << ")"; +} + +void AssemblyWriter::printNonConstVCalls( + const std::vector VCallList, const char *Tag) { + Out << Tag << ": ("; + FieldSeparator FS; + for (auto &VFuncId : VCallList) { + Out << FS; + printVFuncId(VFuncId); + } + Out << ")"; +} + +void AssemblyWriter::printConstVCalls( + const std::vector VCallList, const char *Tag) { + Out << Tag << ": ("; + FieldSeparator FS; + for (auto &ConstVCall : VCallList) { + Out << FS; + printVFuncId(ConstVCall.VFunc); + if (ConstVCall.Args.size()) { + Out << ", "; + printArgs(ConstVCall.Args); + } + } + Out << ")"; +} + +void AssemblyWriter::printSummary(const GlobalValueSummary &Summary) { + GlobalValueSummary::GVFlags GVFlags = Summary.flags(); + GlobalValue::LinkageTypes LT = (GlobalValue::LinkageTypes)GVFlags.Linkage; + Out << getSummaryKindName(Summary.getSummaryKind()) << ": "; + Out << "(module: ^" << TheIndex->getModuleId(Summary.modulePath()) + << ", flags: ("; + Out << "linkage: " + << (LT == GlobalValue::ExternalLinkage ? "extern" + : getLinkagePrintName(LT, false)); + Out << ", notEligibleToImport: " << GVFlags.NotEligibleToImport; + Out << ", live: " << GVFlags.Live; + Out << ", dsoLocal: " << GVFlags.DSOLocal; + Out << ")"; + + if (Summary.getSummaryKind() == GlobalValueSummary::AliasKind) + printAliasSummary(cast(&Summary)); + else if (Summary.getSummaryKind() == GlobalValueSummary::FunctionKind) + printFunctionSummary(cast(&Summary)); + else + printGlobalVarSummary(cast(&Summary)); + + auto RefList = Summary.refs(); + if (RefList.size() > 0) { + Out << ", refs: ("; + FieldSeparator FS; + for (auto &Ref : RefList) { + Out << FS; + Out << "^" << Machine.getGUIDSlot(Ref.getGUID()); + } + Out << ")"; + } + + Out << ")"; +} + +void AssemblyWriter::printSummaryInfo(unsigned Slot, const ValueInfo &VI) { + Out << "^" << Slot << " = gv: ("; + if (!VI.name().empty()) + Out << "name: " << VI.name(); + else + Out << "guid: " << VI.getGUID(); + if (VI.getSummaryList().size() > 0) { + Out << ", summaries: ("; + FieldSeparator FS; + for (auto &Summary : VI.getSummaryList()) { + Out << FS; + printSummary(*Summary); + } + Out << ")"; + } + Out << ")"; + if (!VI.name().empty()) + Out << " ; guid = " << VI.getGUID(); + Out << "\n"; +} + static void printMetadataIdentifier(StringRef Name, formatted_raw_ostream &Out) { if (Name.empty()) { @@ -2509,34 +3012,6 @@ Out << "}\n"; } -static const char *getLinkagePrintName(GlobalValue::LinkageTypes LT) { - switch (LT) { - case GlobalValue::ExternalLinkage: - return ""; - case GlobalValue::PrivateLinkage: - return "private "; - case GlobalValue::InternalLinkage: - return "internal "; - case GlobalValue::LinkOnceAnyLinkage: - return "linkonce "; - case GlobalValue::LinkOnceODRLinkage: - return "linkonce_odr "; - case GlobalValue::WeakAnyLinkage: - return "weak "; - case GlobalValue::WeakODRLinkage: - return "weak_odr "; - case GlobalValue::CommonLinkage: - return "common "; - case GlobalValue::AppendingLinkage: - return "appending "; - case GlobalValue::ExternalWeakLinkage: - return "extern_weak "; - case GlobalValue::AvailableExternallyLinkage: - return "available_externally "; - } - llvm_unreachable("invalid linkage"); -} - static void PrintVisibility(GlobalValue::VisibilityTypes Vis, formatted_raw_ostream &Out) { switch (Vis) { @@ -3676,6 +4151,13 @@ printMetadataImpl(OS, *this, MST, M, /* OnlyAsOperand */ false); } +void ModuleSummaryIndex::print(raw_ostream &ROS, bool IsForDebug) const { + SlotTracker SlotTable(this); + formatted_raw_ostream OS(ROS); + AssemblyWriter W(OS, SlotTable, this, IsForDebug); + W.printModuleSummaryIndex(); +} + #if !defined(NDEBUG) || defined(LLVM_ENABLE_DUMP) // Value::dump - allow easy printing of Values from the debugger. LLVM_DUMP_METHOD @@ -3708,4 +4190,8 @@ print(dbgs(), M, /*IsForDebug=*/true); dbgs() << '\n'; } + +// Allow printing of ModuleSummaryIndex from the debugger. +LLVM_DUMP_METHOD +void ModuleSummaryIndex::dump() const { print(dbgs(), /*IsForDebug=*/true); } #endif Index: test/Bitcode/thinlto-alias.ll =================================================================== --- test/Bitcode/thinlto-alias.ll +++ test/Bitcode/thinlto-alias.ll @@ -2,8 +2,10 @@ ; RUN: opt -module-summary %s -o %t.o ; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s ; RUN: opt -module-summary %p/Inputs/thinlto-alias.ll -o %t2.o +; RUN: llvm-dis -o - %t2.o | FileCheck %s --check-prefix=DIS ; RUN: llvm-lto -thinlto -o %t3 %t.o %t2.o ; RUN: llvm-bcanalyzer -dump %t3.thinlto.bc | FileCheck %s --check-prefix=COMBINED +; RUN: llvm-dis -o - %t3.thinlto.bc | FileCheck %s --check-prefix=COMBINED-DIS ; CHECK: ; COMBINED: @@ -28,3 +30,14 @@ } declare i1 @llvm.type.test(i8*, metadata) nounwind readnone + +; DIS: ^0 = module: (path: {{.*}}/test/Bitcode/Output/thinlto-type-tests.ll.tmp.o, hash: 0) +; DIS: ^1 = gv: (name: llvm.type.test) ; guid = 608142985856744218 +; DIS: ^2 = gv: (name: h, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (16434608426314478903))))) ; guid = 8124147457056772133 +; DIS: ^3 = gv: (name: g, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (6699318081062747564, 16434608426314478903))))) ; guid = 13146401226427987378 +; DIS: ^4 = gv: (name: f, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (6699318081062747564))))) ; guid = 14740650423002898831 + +; COMBINED-DIS: ^0 = module: (path: {{.*}}/test/Bitcode/Output/thinlto-type-tests.ll.tmp.o, hash: 0) +; COMBINED-DIS: ^1 = gv: (guid: 8124147457056772133, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (16434608426314478903))))) +; COMBINED-DIS: ^2 = gv: (guid: 13146401226427987378, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (6699318081062747564, 16434608426314478903))))) +; COMBINED-DIS: ^3 = gv: (guid: 14740650423002898831, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (6699318081062747564))))) Index: test/Bitcode/thinlto-type-vcalls.ll =================================================================== --- test/Bitcode/thinlto-type-vcalls.ll +++ test/Bitcode/thinlto-type-vcalls.ll @@ -1,7 +1,9 @@ ; RUN: opt -module-summary %s -o %t.o ; RUN: llvm-bcanalyzer -dump %t.o | FileCheck %s +; RUN: llvm-dis -o - %t.o | FileCheck %s --check-prefix=DIS ; RUN: llvm-lto -thinlto -o %t2 %t.o ; RUN: llvm-bcanalyzer -dump %t2.thinlto.bc | FileCheck --check-prefix=COMBINED %s +; RUN: llvm-dis -o - %t2.thinlto.bc | FileCheck %s --check-prefix=COMBINED-DIS target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" @@ -103,3 +105,22 @@ declare i1 @llvm.type.test(i8*, metadata) nounwind readnone declare void @llvm.assume(i1) declare {i8*, i1} @llvm.type.checked.load(i8*, i32, metadata) + +; DIS: ^0 = module: (path: {{.*}}/test/Bitcode/Output/thinlto-type-vcalls.ll.tmp.o, hash: 0) +; DIS: ^1 = gv: (name: llvm.type.test) ; guid = 608142985856744218 +; DIS: ^2 = gv: (name: f1, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 2072045998141807037 +; DIS: ^3 = gv: (name: f3, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) ; guid = 4197650231481825559 +; DIS: ^4 = gv: (name: llvm.type.checked.load) ; guid = 5568222536364573403 +; DIS: ^5 = gv: (name: llvm.assume) ; guid = 6385187066495850096 +; DIS: ^6 = gv: (name: f2, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32)))))) ; guid = 8471399308421654326 +; DIS: ^7 = gv: (name: f4, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16), args: (42), vFuncId: (guid: 6699318081062747564, offset: 24), args: (43)))))) ; guid = 10064745020953272174 +; DIS: ^8 = gv: (name: f5, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)))))) ; guid = 11686717102184386164 +; DIS: ^9 = gv: (name: f6, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (7546896869197086323))))) ; guid = 11834966808443348068 + +; COMBINED-DIS: ^0 = module: (path: {{.*}}/test/Bitcode/Output/thinlto-type-vcalls.ll.tmp.o, hash: 0) +; COMBINED-DIS: ^1 = gv: (guid: 2072045998141807037, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 8, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) +; COMBINED-DIS: ^2 = gv: (guid: 4197650231481825559, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16)))))) +; COMBINED-DIS: ^3 = gv: (guid: 8471399308421654326, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeVCalls: (vFuncId: (guid: 6699318081062747564, offset: 24), vFuncId: (guid: 16434608426314478903, offset: 32)))))) +; COMBINED-DIS: ^4 = gv: (guid: 10064745020953272174, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 15, typeIdInfo: (typeTestAssumeConstVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16), args: (42), vFuncId: (guid: 6699318081062747564, offset: 24), args: (43)))))) +; COMBINED-DIS: ^5 = gv: (guid: 11686717102184386164, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 5, typeIdInfo: (typeCheckedLoadConstVCalls: (vFuncId: (guid: 6699318081062747564, offset: 16), args: (42)))))) +; COMBINED-DIS: ^6 = gv: (guid: 11834966808443348068, summaries: (function: (module: ^0, flags: (linkage: extern, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (7546896869197086323))))) Index: tools/llvm-dis/llvm-dis.cpp =================================================================== --- tools/llvm-dis/llvm-dis.cpp +++ tools/llvm-dis/llvm-dis.cpp @@ -146,19 +146,6 @@ static ExitOnError ExitOnErr; -static std::unique_ptr openInputFile(LLVMContext &Context) { - std::unique_ptr MB = - ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); - std::unique_ptr M = ExitOnErr(getOwningLazyBitcodeModule( - std::move(MB), Context, - /*ShouldLazyLoadMetadata=*/true, SetImporting)); - if (MaterializeMetadata) - ExitOnErr(M->materializeMetadata()); - else - ExitOnErr(M->materializeAll()); - return M; -} - int main(int argc, char **argv) { InitLLVM X(argc, argv); @@ -169,7 +156,19 @@ llvm::make_unique(argv[0])); cl::ParseCommandLineOptions(argc, argv, "llvm .bc -> .ll disassembler\n"); - std::unique_ptr M = openInputFile(Context); + std::unique_ptr MB = + ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(InputFilename))); + std::unique_ptr M = ExitOnErr(getLazyBitcodeModule( + *MB, Context, /*ShouldLazyLoadMetadata=*/true, SetImporting)); + if (MaterializeMetadata) + ExitOnErr(M->materializeMetadata()); + else + ExitOnErr(M->materializeAll()); + + BitcodeLTOInfo LTOInfo = ExitOnErr(getBitcodeLTOInfo(*MB)); + std::unique_ptr Index; + if (LTOInfo.HasSummary) + Index = ExitOnErr(getModuleSummaryIndex(*MB)); // Just use stdout. We won't actually print anything on it. if (DontPrint) @@ -198,8 +197,11 @@ Annotator.reset(new CommentWriter()); // All that llvm-dis does is write the assembly to a file. - if (!DontPrint) + if (!DontPrint) { M->print(Out->os(), Annotator.get(), PreserveAssemblyUseListOrder); + if (Index) + Index->print(Out->os()); + } // Declare success. Out->keep(); Index: tools/llvm-lto/llvm-lto.cpp =================================================================== --- tools/llvm-lto/llvm-lto.cpp +++ tools/llvm-lto/llvm-lto.cpp @@ -380,7 +380,7 @@ ExitOnError ExitOnErr("llvm-lto: error loading file '" + Filename + "': "); std::unique_ptr MB = ExitOnErr(errorOrToExpected(MemoryBuffer::getFileOrSTDIN(Filename))); - ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, ++NextModuleId)); + ExitOnErr(readModuleSummaryIndex(*MB, CombinedIndex, NextModuleId++)); } std::error_code EC; assert(!OutputFilename.empty());