From 44e9da29851c63bafefecb13f0ee8583e8f82faf Mon Sep 17 00:00:00 2001 From: Teresa Johnson Date: Sat, 26 May 2018 02:34:13 +0000 Subject: [PATCH] [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 --- docs/LangRef.rst | 304 +++++++++++ include/llvm/IR/ModuleSummaryIndex.h | 54 +- lib/AsmParser/LLLexer.cpp | 50 +- lib/AsmParser/LLLexer.h | 2 + lib/AsmParser/LLParser.cpp | 53 ++ lib/AsmParser/LLParser.h | 2 + lib/AsmParser/LLToken.h | 1 + lib/IR/AsmWriter.cpp | 489 +++++++++++++++++- test/Assembler/thinlto-bad-summary1.ll | 18 + test/Assembler/thinlto-bad-summary2.ll | 18 + test/Assembler/thinlto-bad-summary3.ll | 19 + test/Bitcode/thinlto-alias.ll | 12 + ...ction-summary-callgraph-profile-summary.ll | 36 ++ ...hinlto-function-summary-callgraph-relbf.ll | 6 + .../thinlto-function-summary-refgraph.ll | 15 + test/Bitcode/thinlto-type-tests.ll | 13 + test/Bitcode/thinlto-type-vcalls.ll | 21 + tools/llvm-dis/llvm-dis.cpp | 32 +- 18 files changed, 1084 insertions(+), 61 deletions(-) create mode 100644 test/Assembler/thinlto-bad-summary1.ll create mode 100644 test/Assembler/thinlto-bad-summary2.ll create mode 100644 test/Assembler/thinlto-bad-summary3.ll diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 702df7a52dc..33587eebece 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -5700,6 +5700,310 @@ Each individual option is required to be either a valid option for the target's 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 diff --git a/include/llvm/IR/ModuleSummaryIndex.h b/include/llvm/IR/ModuleSummaryIndex.h index 7dac5b32f80..c496e1e3c1f 100644 --- a/include/llvm/IR/ModuleSummaryIndex.h +++ b/include/llvm/IR/ModuleSummaryIndex.h @@ -428,6 +428,26 @@ public: 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 @@ private: /// 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 @@ public: TIdInfo->TypeTests.push_back(Guid); } + const TypeIdInfo *getTypeIdInfo() const { return TIdInfo.get(); }; + friend struct GraphTraits; }; @@ -865,6 +867,12 @@ public: } 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 @@ public: 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; diff --git a/lib/AsmParser/LLLexer.cpp b/lib/AsmParser/LLLexer.cpp index 54c4c595c34..640b825e485 100644 --- a/lib/AsmParser/LLLexer.cpp +++ b/lib/AsmParser/LLLexer.cpp @@ -219,6 +219,8 @@ lltok::Kind LLLexer::LexToken() { 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 @@ bool LLLexer::ReadVarName() { 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 @@ lltok::Kind LLLexer::LexVar(lltok::Kind Var, lltok::Kind VarID) { 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 @@ lltok::Kind LLLexer::LexExclaim() { 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. diff --git a/lib/AsmParser/LLLexer.h b/lib/AsmParser/LLLexer.h index 90bf17d7a74..48d1e947842 100644 --- a/lib/AsmParser/LLLexer.h +++ b/lib/AsmParser/LLLexer.h @@ -81,10 +81,12 @@ namespace llvm { 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); diff --git a/lib/AsmParser/LLParser.cpp b/lib/AsmParser/LLParser.cpp index afdf542d13f..0d478d4090b 100644 --- a/lib/AsmParser/LLParser.cpp +++ b/lib/AsmParser/LLParser.cpp @@ -282,6 +282,10 @@ bool LLParser::ParseTopLevelEntities() { 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 @@ bool LLParser::ParseStandaloneMetadata() { 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; diff --git a/lib/AsmParser/LLParser.h b/lib/AsmParser/LLParser.h index 79428d4b703..bead02a7690 100644 --- a/lib/AsmParser/LLParser.h +++ b/lib/AsmParser/LLParser.h @@ -312,6 +312,8 @@ namespace llvm { 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); diff --git a/lib/AsmParser/LLToken.h b/lib/AsmParser/LLToken.h index 5e05f54509f..b784b02f309 100644 --- a/lib/AsmParser/LLToken.h +++ b/lib/AsmParser/LLToken.h @@ -351,6 +351,7 @@ enum Kind { GlobalID, // @42 LocalVarID, // %42 AttrGrpID, // #42 + SummaryID, // ^42 // String valued tokens (StrVal). LabelStr, // foo: diff --git a/lib/IR/AsmWriter.cpp b/lib/IR/AsmWriter.cpp index 359ca1d5054..7659a6b580d 100644 --- a/lib/IR/AsmWriter.cpp +++ b/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 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 @@ 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::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, 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 SlotTrackerStorage; SlotTracker &Machine; TypePrinting TypePrinter; - AssemblyAnnotationWriter *AnnotationWriter; + AssemblyAnnotationWriter *AnnotationWriter = nullptr; SetVector Comdats; bool IsForDebug; bool ShouldPreserveUseListOrder; @@ -2205,6 +2267,7 @@ class AssemblyWriter { SmallVector MDNames; /// Synchronization scope names registered with LLVMContext. SmallVector SSNs; + DenseMap 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 &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 @@ 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 << ""; @@ -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> 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 @@ 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 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 @@ 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 diff --git a/test/Assembler/thinlto-bad-summary1.ll b/test/Assembler/thinlto-bad-summary1.ll new file mode 100644 index 00000000000..394506317d9 --- /dev/null +++ b/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 = () diff --git a/test/Assembler/thinlto-bad-summary2.ll b/test/Assembler/thinlto-bad-summary2.ll new file mode 100644 index 00000000000..b736c101adc --- /dev/null +++ b/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: )( diff --git a/test/Assembler/thinlto-bad-summary3.ll b/test/Assembler/thinlto-bad-summary3.ll new file mode 100644 index 00000000000..1855a3d4dd8 --- /dev/null +++ b/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 diff --git a/test/Bitcode/thinlto-alias.ll b/test/Bitcode/thinlto-alias.ll index a5020ff7119..fd4c760a2eb 100644 --- a/test/Bitcode/thinlto-alias.ll +++ b/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 @@ define i1 @h() { } 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))))) diff --git a/test/Bitcode/thinlto-type-vcalls.ll b/test/Bitcode/thinlto-type-vcalls.ll index 40d229d1214..ff53acd7aee 100644 --- a/test/Bitcode/thinlto-type-vcalls.ll +++ b/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 @@ define {i8*, i1} @f6(i8* %vtable) { 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))))) diff --git a/tools/llvm-dis/llvm-dis.cpp b/tools/llvm-dis/llvm-dis.cpp index f254a761f87..8143a2a5a93 100644 --- a/tools/llvm-dis/llvm-dis.cpp +++ b/tools/llvm-dis/llvm-dis.cpp @@ -147,19 +147,6 @@ struct LLVMDisDiagnosticHandler : public DiagnosticHandler { 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 @@ int main(int argc, char **argv) { 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 @@ int main(int argc, char **argv) { 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();