Index: llvm/trunk/docs/LangRef.rst =================================================================== --- llvm/trunk/docs/LangRef.rst +++ llvm/trunk/docs/LangRef.rst @@ -5700,6 +5700,310 @@ linker, or an option that is reserved by the target specific assembly writer or object file emitter. No other aspect of these options is defined by the IR. +.. _summary: + +ThinLTO Summary +=============== + +Compiling with `ThinLTO `_ +causes the building of a compact summary of the module that is emitted into +the bitcode. The summary is emitted into the LLVM assembly and identified +in syntax by a caret ('``^``'). + +*Note that temporarily the summary entries are skipped when parsing the +assembly, although the parsing support is actively being implemented. The +following describes when the summary entries will be parsed once implemented.* +The summary will be parsed into a ModuleSummaryIndex object under the +same conditions where summary index is currently built from bitcode. +Specifically, tools that test the Thin Link portion of a ThinLTO compile +(i.e. llvm-lto and llvm-lto2), or when parsing a combined index +for a distributed ThinLTO backend via clang's "``-fthinlto-index=<>``" flag. +Additionally, it will be parsed into a bitcode output, along with the Module +IR, via the "``llvm-as``" tool. Tools that parse the Module IR for the purposes +of optimization (e.g. "``clang -x ir``" and "``opt``"), will ignore the +summary entries (just as they currently ignore summary entries in a bitcode +input file). + +There are currently 3 types of summary entries in the LLVM assembly: +:ref:`module paths`, +:ref:`global values`, and +:ref:`type identifiers`. + +.. _module_path_summary: + +Module Path Summary Entry +------------------------- + +Each module path summary entry lists a module containing global values included +in the summary. For a single IR module there will be one such entry, but +in a combined summary index produced during the thin link, there will be +one module path entry per linked module with summary. + +Example: + +.. code-block:: llvm + + ^0 = module: (path: "/path/to/file.o", hash: (2468601609, 1329373163, 1565878005, 638838075, 3148790418)) + +The ``path`` field is a string path to the bitcode file, and the ``hash`` +field is the 160-bit SHA-1 hash of the IR bitcode contents, used for +incremental builds and caching. + +.. _gv_summary: + +Global Value Summary Entry +-------------------------- + +Each global value summary entry corresponds to a global value defined or +referenced by a summarized module. + +Example: + +.. code-block:: llvm + + ^4 = gv: (name: "f"[, summaries: (Summary)[, (Summary)]*]?) ; guid = 14740650423002898831 + +For declarations, there will not be a summary list. For definitions, a +global value will contain a list of summaries, one per module containing +a definition. There can be multiple entries in a combined summary index +for symbols with weak linkage. + +Each ``Summary`` format will depend on whether the global value is a +:ref:`function`, :ref:`variable`, or +:ref:`alias`. + +.. _function_summary: + +Function Summary +^^^^^^^^^^^^^^^^ + +If the global value is a function, the ``Summary`` entry will look like: + +.. code-block:: llvm + + function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2[, FuncFlags]?[, Calls]?[, TypeIdInfo]?[, Refs]? + +The ``module`` field includes the summary entry id for the module containing +this definition, and the ``flags`` field contains information such as +the linkage type, a flag indicating whether it is legal to import the +definition, whether it is globally live and whether the linker resolved it +to a local definition (the latter two are populated during the thin link). +The ``insts`` field contains the number of IR instructions in the function. +Finally, there are several optional fields: :ref:`FuncFlags`, +:ref:`Calls`, :ref:`TypeIdInfo`, +:ref:`Refs`. + +.. _variable_summary: + +Global Variable Summary +^^^^^^^^^^^^^^^^^^^^^^^ + +If the global value is a variable, the ``Summary`` entry will look like: + +.. code-block:: llvm + + variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0)[, Refs]? + +The variable entry contains a subset of the fields in a +:ref:`function summary `, see the descriptions there. + +.. _alias_summary: + +Alias Summary +^^^^^^^^^^^^^ + +If the global value is an alias, the ``Summary`` entry will look like: + +.. code-block:: llvm + + alias: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), aliasee: ^2) + +The ``module`` and ``flags`` fields are as described for a +:ref:`function summary `. The ``aliasee`` field +contains a reference to the global value summary entry of the aliasee. + +.. _funcflags_summary: + +Function Flags +^^^^^^^^^^^^^^ + +The optional ``FuncFlags`` field looks like: + +.. code-block:: llvm + + funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0) + +If unspecified, flags are assumed to hold the conservative ``false`` value of +``0``. + +.. _calls_summary: + +Calls +^^^^^ + +The optional ``Calls`` field looks like: + +.. code-block:: llvm + + calls: ((Callee)[, (Callee)]*) + +where each ``Callee`` looks like: + +.. code-block:: llvm + + callee: ^1[, hotness: None]?[, relbf: 0]? + +The ``callee`` refers to the summary entry id of the callee. At most one +of ``hotness`` (which can take the values ``Unknown``, ``Cold``, ``None``, +``Hot``, and ``Critical``), and ``relbf`` (which holds the integer +branch frequency relative to the entry frequency, scaled down by 2^8) +may be specified. The defaults are ``Unknown`` and ``0``, respectively. + +.. _refs_summary: + +Refs +^^^^ + +The optional ``Refs`` field looks like: + +.. code-block:: llvm + + refs: ((Ref)[, (Ref)]*) + +where each ``Ref`` contains a reference to the summary id of the referenced +value (e.g. ``^1``). + +.. _typeidinfo_summary: + +TypeIdInfo +^^^^^^^^^^ + +The optional ``TypeIdInfo`` field, used for +`Control Flow Integrity `_, +looks like: + +.. code-block:: llvm + + typeIdInfo: [(TypeTests)]?[, (TypeTestAssumeVCalls)]?[, (TypeCheckedLoadVCalls)]?[, (TypeTestAssumeConstVCalls)]?[, (TypeCheckedLoadConstVCalls)]? + +These optional fields have the following forms: + +TypeTests +""""""""" + +.. code-block:: llvm + + typeTests: (TypeIdRef[, TypeIdRef]*) + +Where each ``TypeIdRef`` refers to a :ref:`type id` +by summary id or ``GUID``. + +TypeTestAssumeVCalls +"""""""""""""""""""" + +.. code-block:: llvm + + typeTestAssumeVCalls: (VFuncId[, VFuncId]*) + +Where each VFuncId has the format: + +.. code-block:: llvm + + vFuncId: (TypeIdRef, offset: 16) + +Where each ``TypeIdRef`` refers to a :ref:`type id` +by summary id or ``GUID`` preceeded by a ``guid:`` tag. + +TypeCheckedLoadVCalls +""""""""""""""""""""" + +.. code-block:: llvm + + typeCheckedLoadVCalls: (VFuncId[, VFuncId]*) + +Where each VFuncId has the format described for ``TypeTestAssumeVCalls``. + +TypeTestAssumeConstVCalls +""""""""""""""""""""""""" + +.. code-block:: llvm + + typeTestAssumeConstVCalls: (ConstVCall[, ConstVCall]*) + +Where each ConstVCall has the format: + +.. code-block:: llvm + + VFuncId, args: (Arg[, Arg]*) + +and where each VFuncId has the format described for ``TypeTestAssumeVCalls``, +and each Arg is an integer argument number. + +TypeCheckedLoadConstVCalls +"""""""""""""""""""""""""" + +.. code-block:: llvm + + typeCheckedLoadConstVCalls: (ConstVCall[, ConstVCall]*) + +Where each ConstVCall has the format described for +``TypeTestAssumeConstVCalls``. + +.. _typeid_summary: + +Type ID Summary Entry +--------------------- + +Each type id summary entry corresponds to a type identifier resolution +which is generated during the LTO link portion of the compile when building +with `Control Flow Integrity `_, +so these are only present in a combined summary index. + +Example: + +.. code-block:: llvm + + ^4 = typeid: (name: "_ZTS1A", summary: (typeTestRes: (kind: allOnes, sizeM1BitWidth: 7[, alignLog2: 0]?[, sizeM1: 0]?[, bitMask: 0]?[, inlineBits: 0]?)[, WpdResolutions]?)) ; guid = 7004155349499253778 + +The ``typeTestRes`` gives the type test resolution ``kind`` (which may +be ``unsat``, ``byteArray``, ``inline``, ``single``, or ``allOnes``), and +the ``size-1`` bit width. It is followed by optional flags, which default to 0, +and an optional WpdResolutions (whole program devirtualization resolution) +field that looks like: + +.. code-block:: llvm + + wpdResolutions: ((offset: 0, WpdRes)[, (offset: 1, WpdRes)]* + +where each entry is a mapping from the given byte offset to the whole-program +devirtualization resolution WpdRes, that has one of the following formats: + +.. code-block:: llvm + + wpdRes: (kind: branchFunnel) + wpdRes: (kind: singleImpl, singleImplName: "_ZN1A1nEi") + wpdRes: (kind: indir) + +Additionally, each wpdRes has an optional ``resByArg`` field, which +describes the resolutions for calls with all constant integer arguments: + +.. code-block:: llvm + + resByArg: (ResByArg[, ResByArg]*) + +where ResByArg is: + +.. code-block:: llvm + + args: (Arg[, Arg]*), byArg: (kind: UniformRetVal[, info: 0][, byte: 0][, bit: 0]) + +Where the ``kind`` can be ``Indir``, ``UniformRetVal``, ``UniqueRetVal`` +or ``VirtualConstProp``. The ``info`` field is only used if the kind +is ``UniformRetVal`` (indicates the uniform return value), or +``UniqueRetVal`` (holds the return value associated with the unique vtable +(0 or 1)). The ``byte`` and ``bit`` fields are only used if the target does +not support the use of absolute symbols to store constants. + .. _intrinsicglobalvariables: Intrinsic Global Variables Index: llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h @@ -428,6 +428,26 @@ 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 referenced by something other than 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,26 +488,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 referenced by something other than 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: @@ -577,6 +577,8 @@ TIdInfo->TypeTests.push_back(Guid); } + const TypeIdInfo *getTypeIdInfo() const { return TIdInfo.get(); }; + friend struct GraphTraits; }; @@ -865,6 +867,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); @@ -1049,6 +1057,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: llvm/trunk/lib/AsmParser/LLLexer.h =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.h +++ llvm/trunk/lib/AsmParser/LLLexer.h @@ -81,10 +81,12 @@ lltok::Kind LexDollar(); lltok::Kind LexExclaim(); lltok::Kind LexPercent(); + lltok::Kind LexUIntID(lltok::Kind Token); lltok::Kind LexVar(lltok::Kind Var, lltok::Kind VarID); 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: llvm/trunk/lib/AsmParser/LLLexer.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLLexer.cpp +++ llvm/trunk/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': @@ -328,6 +330,22 @@ return false; } +// Lex an ID: [0-9]+. On success, the ID is stored in UIntVal and Token is +// returned, otherwise the Error token is returned. +lltok::Kind LLLexer::LexUIntID(lltok::Kind Token) { + if (!isdigit(static_cast(CurPtr[0]))) + return lltok::Error; + + 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 Token; +} + lltok::Kind LLLexer::LexVar(lltok::Kind Var, lltok::Kind VarID) { // Handle StringConstant: \"[^\"]*\" if (CurPtr[0] == '"') { @@ -357,17 +375,7 @@ return Var; // Handle VarID: [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 VarID; - } - return lltok::Error; + return LexUIntID(VarID); } /// Lex all tokens that start with a % character. @@ -420,22 +428,18 @@ return lltok::exclaim; } +/// Lex all tokens that start with a ^ character. +/// SummaryID ::= ^[0-9]+ +lltok::Kind LLLexer::LexCaret() { + // Handle SummaryID: ^[0-9]+ + return LexUIntID(lltok::SummaryID); +} + /// Lex all tokens that start with a # character. /// AttrGrpID ::= #[0-9]+ lltok::Kind LLLexer::LexHash() { // Handle AttrGrpID: #[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::AttrGrpID; - } - - return lltok::Error; + return LexUIntID(lltok::AttrGrpID); } /// Lex a label, integer type, keyword, or hexadecimal integer constant. Index: llvm/trunk/lib/AsmParser/LLParser.h =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.h +++ llvm/trunk/lib/AsmParser/LLParser.h @@ -312,6 +312,8 @@ bool ParseFnAttributeValuePairs(AttrBuilder &B, std::vector &FwdRefAttrGrps, bool inAttrGrp, LocTy &BuiltinLoc); + bool SkipModuleSummaryEntry(); + bool ParseSummaryEntry(); // Type Parsing. bool ParseType(Type *&Result, const Twine &Msg, bool AllowVoid = false); Index: llvm/trunk/lib/AsmParser/LLParser.cpp =================================================================== --- llvm/trunk/lib/AsmParser/LLParser.cpp +++ llvm/trunk/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,55 @@ return false; } +// Skips a single module summary entry. +bool LLParser::SkipModuleSummaryEntry() { + // Each module summary entry consists of a tag for the entry + // type, followed by a colon, then the fields surrounded by nested sets of + // parentheses. The "tag:" looks like a Label. Once parsing support is + // in place we will look for the tokens corresponding to the expected tags. + if (ParseToken(lltok::LabelStr, + "expected 'label' at start of summary entry") || + ParseToken(lltok::lparen, "expected '(' at start of summary entry")) + return true; + // Now walk through the parenthesized entry, until the number of open + // parentheses goes back down to 0 (the first '(' was parsed above). + unsigned NumOpenParen = 1; + do { + switch (Lex.getKind()) { + case lltok::lparen: + NumOpenParen++; + break; + case lltok::rparen: + NumOpenParen--; + break; + case lltok::Eof: + return TokError("found end of file while parsing summary entry"); + default: + // Skip everything in between parentheses. + break; + } + Lex.Lex(); + } while (NumOpenParen > 0); + 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. + if (SkipModuleSummaryEntry()) + return true; + return false; +} + static bool isValidVisibilityForLinkage(unsigned V, unsigned L) { return !GlobalValue::isLocalLinkage((GlobalValue::LinkageTypes)L) || (GlobalValue::VisibilityTypes)V == GlobalValue::DefaultVisibility; Index: llvm/trunk/lib/AsmParser/LLToken.h =================================================================== --- llvm/trunk/lib/AsmParser/LLToken.h +++ llvm/trunk/lib/AsmParser/LLToken.h @@ -351,6 +351,7 @@ GlobalID, // @42 LocalVarID, // %42 AttrGrpID, // #42 + SummaryID, // ^42 // String valued tokens (StrVal). LabelStr, // foo: Index: llvm/trunk/lib/IR/AsmWriter.cpp =================================================================== --- llvm/trunk/lib/IR/AsmWriter.cpp +++ llvm/trunk/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 @@ 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 @@ 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. /// @@ -709,6 +717,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; @@ -718,6 +729,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. @@ -749,8 +761,12 @@ 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; + + /// These functions do the actual initialization. inline void initialize(); + void initializeIndex(); // Implementation Details private: @@ -766,9 +782,12 @@ /// 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 @@ : 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 @@ 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 @@ 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, 4> MDs; GO.getAllMetadata(MDs); @@ -1053,6 +1100,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!"); @@ -1113,6 +1169,11 @@ 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 @@ class AssemblyWriter { formatted_raw_ostream &Out; - const Module *TheModule; + const Module *TheModule = nullptr; + const ModuleSummaryIndex *TheIndex = nullptr; std::unique_ptr SlotTrackerStorage; SlotTracker &Machine; TypePrinting TypePrinter; - AssemblyAnnotationWriter *AnnotationWriter; + AssemblyAnnotationWriter *AnnotationWriter = nullptr; SetVector Comdats; bool IsForDebug; bool ShouldPreserveUseListOrder; @@ -2205,6 +2267,7 @@ SmallVector MDNames; /// Synchronization scope names registered with LLVMContext. SmallVector SSNs; + DenseMap SummaryToGUIDMap; public: /// Construct an AssemblyWriter with an external SlotTracker @@ -2212,6 +2275,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); @@ -2247,6 +2313,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: /// Print out metadata attachments. void printMetadataAttachments( @@ -2277,6 +2362,11 @@ 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 << ""; @@ -2478,6 +2568,210 @@ } } +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> 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 &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 @@ 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 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.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(&Summary)); + else if (Summary.getSummaryKind() == GlobalValueSummary::FunctionKind) + printFunctionSummary(cast(&Summary)); + else + printGlobalVarSummary(cast(&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 @@ 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 @@ 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: llvm/trunk/test/Assembler/thinlto-bad-summary1.ll =================================================================== --- llvm/trunk/test/Assembler/thinlto-bad-summary1.ll +++ llvm/trunk/test/Assembler/thinlto-bad-summary1.ll @@ -0,0 +1,18 @@ +; Test that we get appropriate error when parsing summary with a missing +; summary type label. +; RUN: not llvm-as %s 2>&1 | FileCheck %s + +; CHECK: error: expected 'label' at start of summary entry + +; 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() #0 { +entry: + ret i32 1 +} + +^0 = module: (path: "{{.*}}/test/Assembler/thinlto-bad-summary1.ll", hash: (0, 0, 0, 0, 0)) +^1 = () Index: llvm/trunk/test/Assembler/thinlto-bad-summary2.ll =================================================================== --- llvm/trunk/test/Assembler/thinlto-bad-summary2.ll +++ llvm/trunk/test/Assembler/thinlto-bad-summary2.ll @@ -0,0 +1,18 @@ +; Test that we get appropriate error when parsing summary that doesn't +; have a '(' after the summary type label. +; RUN: not llvm-as %s 2>&1 | FileCheck %s + +; CHECK: error: expected '(' at start of summary entry + +; 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() #0 { +entry: + ret i32 1 +} + +^0 = module: (path: "{{.*}}/test/Assembler/thinlto-bad-summary1.ll", hash: (0, 0, 0, 0, 0)) +^1 = gv: )( Index: llvm/trunk/test/Assembler/thinlto-bad-summary3.ll =================================================================== --- llvm/trunk/test/Assembler/thinlto-bad-summary3.ll +++ llvm/trunk/test/Assembler/thinlto-bad-summary3.ll @@ -0,0 +1,19 @@ +; Test that we get appropriate error when parsing summary with unbalanced +; parentheses. +; RUN: not llvm-as %s 2>&1 | FileCheck %s + +; CHECK: error: found end of file while parsing summary entry + +; 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() #0 { +entry: + ret i32 1 +} + +^0 = module: (path: "{{.*}}/test/Assembler/thinlto-bad-summary1.ll", hash: (0, 0, 0, 0, 0)) +; Missing a ')' +^1 = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1)) ; guid = 15822663052811949562 Index: llvm/trunk/test/Bitcode/thinlto-alias.ll =================================================================== --- llvm/trunk/test/Bitcode/thinlto-alias.ll +++ llvm/trunk/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, 0, 0, 0, 0)) +; DIS: ^1 = gv: (name: "llvm.type.test") ; guid = 608142985856744218 +; DIS: ^2 = gv: (name: "h", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (16434608426314478903))))) ; guid = 8124147457056772133 +; DIS: ^3 = gv: (name: "g", summaries: (function: (module: ^0, flags: (linkage: external, 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: external, 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, 0, 0, 0, 0)) +; COMBINED-DIS: ^1 = gv: (guid: 8124147457056772133, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (16434608426314478903))))) +; COMBINED-DIS: ^2 = gv: (guid: 13146401226427987378, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 4, typeIdInfo: (typeTests: (6699318081062747564, 16434608426314478903))))) +; COMBINED-DIS: ^3 = gv: (guid: 14740650423002898831, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (6699318081062747564))))) Index: llvm/trunk/test/Bitcode/thinlto-type-vcalls.ll =================================================================== --- llvm/trunk/test/Bitcode/thinlto-type-vcalls.ll +++ llvm/trunk/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, 0, 0, 0, 0)) +; DIS: ^1 = gv: (name: "llvm.type.test") ; guid = 608142985856744218 +; DIS: ^2 = gv: (name: "f1", summaries: (function: (module: ^0, flags: (linkage: external, 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: external, 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: external, 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: external, 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: external, 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: external, 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, 0, 0, 0, 0)) +; COMBINED-DIS: ^1 = gv: (guid: 2072045998141807037, summaries: (function: (module: ^0, flags: (linkage: external, 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: external, 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: external, 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: external, 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: external, 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: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 2, typeIdInfo: (typeTests: (7546896869197086323))))) Index: llvm/trunk/tools/llvm-dis/llvm-dis.cpp =================================================================== --- llvm/trunk/tools/llvm-dis/llvm-dis.cpp +++ llvm/trunk/tools/llvm-dis/llvm-dis.cpp @@ -147,19 +147,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); @@ -170,7 +157,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) @@ -199,8 +198,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();