diff options
author | Teresa Johnson <tejohnson@google.com> | 2018-05-26 02:34:13 +0000 |
---|---|---|
committer | Teresa Johnson <tejohnson@google.com> | 2018-05-26 02:34:13 +0000 |
commit | 08d5b4ef0d0839761e3b7546efcd3ee0454742ea (patch) | |
tree | 1962452dfea56ccff172d7612ec1a8c982de27e2 /llvm/lib/IR/AsmWriter.cpp | |
parent | ffebfe10c1cfc2eae92ddd0dc1b212595ac47864 (diff) | |
download | bcm5719-llvm-08d5b4ef0d0839761e3b7546efcd3ee0454742ea.tar.gz bcm5719-llvm-08d5b4ef0d0839761e3b7546efcd3ee0454742ea.zip |
[ThinLTO] Print module summary index to assembly
Summary:
Implements AsmWriter support for printing the module summary index to
assembly with the format discussed in the RFC "LLVM Assembly format for
ThinLTO Summary".
Implements just enough of the parsing support to recognize and ignore
the summary entries. As agreed in the RFC thread, this will be the
behavior when assembling the IR. A follow on change will implement
parsing/assembling of the summary entries for use by tools that
currently build the summary index from bitcode.
Reviewers: dexonsmith, pcc
Subscribers: inglorion, eraman, steven_wu, dblaikie, llvm-commits
Differential Revision: https://reviews.llvm.org/D46699
llvm-svn: 333335
Diffstat (limited to 'llvm/lib/IR/AsmWriter.cpp')
-rw-r--r-- | llvm/lib/IR/AsmWriter.cpp | 489 |
1 files changed, 486 insertions, 3 deletions
diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 359ca1d5054..7659a6b580d 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -59,6 +59,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" @@ -676,6 +677,9 @@ private: 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; @@ -692,6 +696,10 @@ private: DenseMap<AttributeSet, unsigned> asMap; unsigned asNext = 0; + /// GUIDMap - The slot map for GUIDs used in the summary index. + DenseMap<GlobalValue::GUID, unsigned> GUIDMap; + unsigned GUIDNext = 0; + public: /// Construct from a module. /// @@ -709,6 +717,9 @@ public: 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; @@ -718,6 +729,7 @@ public: 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. @@ -749,8 +761,12 @@ public: 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<GlobalValue::GUID, unsigned>::iterator; + + /// These functions do the actual initialization. inline void initialize(); + void initializeIndex(); // Implementation Details private: @@ -766,9 +782,12 @@ private: /// 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(); @@ -869,6 +888,9 @@ SlotTracker::SlotTracker(const Function *F, bool ShouldInitializeAllMetadata) : 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(); @@ -879,6 +901,13 @@ inline void SlotTracker::initialize() { processFunction(); } +void SlotTracker::initializeIndex() { + if (!TheIndex) + return; + 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() { @@ -970,6 +999,24 @@ void SlotTracker::processFunction() { 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"); + assert(TheIndex); + + // 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<std::pair<unsigned, MDNode *>, 4> MDs; GO.getAllMetadata(MDs); @@ -1053,6 +1100,15 @@ int SlotTracker::getAttributeGroupSlot(AttributeSet AS) { 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!"); @@ -1113,6 +1169,11 @@ void SlotTracker::CreateAttributeSetSlot(AttributeSet AS) { asMap[AS] = DestSlot; } +/// Create a new slot for the specified GUID +void SlotTracker::CreateGUIDSlot(GlobalValue::GUID GUID) { + GUIDMap[GUID] = GUIDNext++; +} + //===----------------------------------------------------------------------===// // AsmWriter Implementation //===----------------------------------------------------------------------===// @@ -2193,11 +2254,12 @@ namespace { class AssemblyWriter { formatted_raw_ostream &Out; - const Module *TheModule; + const Module *TheModule = nullptr; + const ModuleSummaryIndex *TheIndex = nullptr; std::unique_ptr<SlotTracker> SlotTrackerStorage; SlotTracker &Machine; TypePrinting TypePrinter; - AssemblyAnnotationWriter *AnnotationWriter; + AssemblyAnnotationWriter *AnnotationWriter = nullptr; SetVector<const Comdat *> Comdats; bool IsForDebug; bool ShouldPreserveUseListOrder; @@ -2205,6 +2267,7 @@ class AssemblyWriter { SmallVector<StringRef, 8> MDNames; /// Synchronization scope names registered with LLVMContext. SmallVector<StringRef, 8> SSNs; + DenseMap<const GlobalValueSummary *, GlobalValue::GUID> SummaryToGUIDMap; public: /// Construct an AssemblyWriter with an external SlotTracker @@ -2212,6 +2275,9 @@ public: 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); @@ -2247,6 +2313,25 @@ public: 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<uint64_t> &Args); + void printWPDRes(const WholeProgramDevirtResolution &WPDRes); + void printTypeIdInfo(const FunctionSummary::TypeIdInfo &TIDInfo); + void printVFuncId(const FunctionSummary::VFuncId VFId); + void + printNonConstVCalls(const std::vector<FunctionSummary::VFuncId> VCallList, + const char *Tag); + void + printConstVCalls(const std::vector<FunctionSummary::ConstVCall> VCallList, + const char *Tag); + private: /// Print out metadata attachments. void printMetadataAttachments( @@ -2277,6 +2362,11 @@ AssemblyWriter::AssemblyWriter(formatted_raw_ostream &o, SlotTracker &Mac, Comdats.insert(C); } +AssemblyWriter::AssemblyWriter(formatted_raw_ostream &o, SlotTracker &Mac, + const ModuleSummaryIndex *Index, bool IsForDebug) + : Out(o), TheIndex(Index), Machine(Mac), TypePrinter(/*Module=*/nullptr), + IsForDebug(IsForDebug), ShouldPreserveUseListOrder(false) {} + void AssemblyWriter::writeOperand(const Value *Operand, bool PrintType) { if (!Operand) { Out << "<null operand!>"; @@ -2478,6 +2568,210 @@ void AssemblyWriter::printModule(const Module *M) { } } +void AssemblyWriter::printModuleSummaryIndex() { + assert(TheIndex); + Machine.initializeIndex(); + + Out << "\n"; + + // 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<std::pair<StringRef, ModuleHash>> 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: ("; + FieldSeparator FS; + for (auto Hash : ModPair.second) + Out << FS << Hash; + 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; + } + + // Print the global value summary entries. + for (auto &GlobalList : *TheIndex) { + auto GUID = GlobalList.first; + auto VI = TheIndex->getValueInfo(GlobalList); + printSummaryInfo(Machine.getGUIDSlot(GUID), VI); + } + + // 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) + Out << ", alignLog2: " << TTRes.AlignLog2; + if (TTRes.SizeM1) + Out << ", sizeM1: " << TTRes.SizeM1; + if (TTRes.BitMask) + // BitMask is uint8_t which causes it to print the corresponding char. + Out << ", bitMask: " << (unsigned)TTRes.BitMask; + if (TTRes.InlineBits) + Out << ", inlineBits: " << TTRes.InlineBits; + + Out << ")"; +} + +void AssemblyWriter::printTypeIdSummary(const TypeIdSummary &TIS) { + Out << ", summary: ("; + printTypeTestResolution(TIS.TTRes); + if (!TIS.WPDRes.empty()) { + 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<uint64_t> &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.empty()) { + 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 getLinkageName(GlobalValue::LinkageTypes LT) { switch (LT) { case GlobalValue::ExternalLinkage: @@ -2515,6 +2809,184 @@ static std::string getLinkageNameWithSpace(GlobalValue::LinkageTypes LT) { return getLinkageName(LT) + " "; } +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().empty()) { + Out << ", calls: ("; + FieldSeparator IFS; + for (auto &Call : FS->calls()) { + Out << IFS; + Out << "(callee: ^" << Machine.getGUIDSlot(Call.first.getGUID()); + if (Call.second.getHotness() != CalleeInfo::HotnessType::Unknown) + Out << ", hotness: " << getHotnessName(Call.second.getHotness()); + else 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.empty()) { + 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.empty()) { + Out << TIDFS; + printNonConstVCalls(TIDInfo.TypeTestAssumeVCalls, "typeTestAssumeVCalls"); + } + if (!TIDInfo.TypeCheckedLoadVCalls.empty()) { + Out << TIDFS; + printNonConstVCalls(TIDInfo.TypeCheckedLoadVCalls, "typeCheckedLoadVCalls"); + } + if (!TIDInfo.TypeTestAssumeConstVCalls.empty()) { + Out << TIDFS; + printConstVCalls(TIDInfo.TypeTestAssumeConstVCalls, + "typeTestAssumeConstVCalls"); + } + if (!TIDInfo.TypeCheckedLoadConstVCalls.empty()) { + 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<FunctionSummary::VFuncId> VCallList, const char *Tag) { + Out << Tag << ": ("; + FieldSeparator FS; + for (auto &VFuncId : VCallList) { + Out << FS; + printVFuncId(VFuncId); + } + Out << ")"; +} + +void AssemblyWriter::printConstVCalls( + const std::vector<FunctionSummary::ConstVCall> VCallList, const char *Tag) { + Out << Tag << ": ("; + FieldSeparator FS; + for (auto &ConstVCall : VCallList) { + Out << FS; + printVFuncId(ConstVCall.VFunc); + if (!ConstVCall.Args.empty()) { + 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: " << getLinkageName(LT); + Out << ", notEligibleToImport: " << GVFlags.NotEligibleToImport; + Out << ", live: " << GVFlags.Live; + Out << ", dsoLocal: " << GVFlags.DSOLocal; + Out << ")"; + + if (Summary.getSummaryKind() == GlobalValueSummary::AliasKind) + printAliasSummary(cast<AliasSummary>(&Summary)); + else if (Summary.getSummaryKind() == GlobalValueSummary::FunctionKind) + printFunctionSummary(cast<FunctionSummary>(&Summary)); + else + printGlobalVarSummary(cast<GlobalVarSummary>(&Summary)); + + auto RefList = Summary.refs(); + if (!RefList.empty()) { + 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().empty()) { + 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()) { @@ -3700,6 +4172,13 @@ void Metadata::print(raw_ostream &OS, ModuleSlotTracker &MST, 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 @@ -3732,4 +4211,8 @@ void Metadata::dump(const Module *M) const { 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 |