Index: include/llvm/AsmParser/Parser.h =================================================================== --- include/llvm/AsmParser/Parser.h +++ include/llvm/AsmParser/Parser.h @@ -21,11 +21,12 @@ class Constant; class LLVMContext; class Module; +class ModuleSummaryIndex; struct SlotMapping; class SMDiagnostic; class Type; -/// This function is the main interface to the LLVM Assembly Parser. It parses +/// This function is a main interface to the LLVM Assembly Parser. It parses /// an ASCII file that (presumably) contains LLVM Assembly code. It returns a /// Module (intermediate representation) with the corresponding features. Note /// that this does not verify that the generated Module is valid, so you should @@ -67,6 +68,46 @@ bool UpgradeDebugInfo = true, StringRef DataLayoutString = ""); +/// Holds the Module and ModuleSummaryIndex returned by the interfaces +/// that parse both. +struct ParsedModuleAndIndex { + std::unique_ptr Mod; + std::unique_ptr Index; +}; + +/// This function is a main interface to the LLVM Assembly Parser. It parses +/// an ASCII file that (presumably) contains LLVM Assembly code, including +/// a module summary. It returns a Module (intermediate representation) and +/// a ModuleSummaryIndex with the corresponding features. Note that this does +/// not verify that the generated Module or Index are valid, so you should +/// run the verifier after parsing the file to check that they are okay. +/// Parse LLVM Assembly from a file +/// \param Filename The name of the file to parse +/// \param Error Error result info. +/// \param Context Context in which to allocate globals info. +/// \param Slots The optional slot mapping that will be initialized during +/// parsing. +/// \param UpgradeDebugInfo Run UpgradeDebugInfo, which runs the Verifier. +/// This option should only be set to false by llvm-as +/// for use inside the LLVM testuite! +/// \param DataLayoutString Override datalayout in the llvm assembly. +ParsedModuleAndIndex +parseAssemblyFileWithIndex(StringRef Filename, SMDiagnostic &Error, + LLVMContext &Context, SlotMapping *Slots = nullptr, + bool UpgradeDebugInfo = true, + StringRef DataLayoutString = ""); + +/// This function is a main interface to the LLVM Assembly Parser. It parses +/// an ASCII file that (presumably) contains LLVM Assembly code for a module +/// summary. It returns a a ModuleSummaryIndex with the corresponding features. +/// Note that this does not verify that the generated Index is valid, so you +/// should run the verifier after parsing the file to check that it is okay. +/// Parse LLVM Assembly Index from a file +/// \param Filename The name of the file to parse +/// \param Error Error result info. +std::unique_ptr +parseSummaryIndexAssemblyFile(StringRef Filename, SMDiagnostic &Error); + /// parseAssemblyFile and parseAssemblyString are wrappers around this function. /// Parse LLVM Assembly from a MemoryBuffer. /// \param F The MemoryBuffer containing assembly @@ -83,6 +124,34 @@ bool UpgradeDebugInfo = true, StringRef DataLayoutString = ""); +/// Parse LLVM Assembly including the summary index from a MemoryBuffer. +/// +/// \param F The MemoryBuffer containing assembly with summary +/// \param Err Error result info. +/// \param Slots The optional slot mapping that will be initialized during +/// parsing. +/// \param UpgradeDebugInfo Run UpgradeDebugInfo, which runs the Verifier. +/// This option should only be set to false by llvm-as +/// for use inside the LLVM testuite! +/// \param DataLayoutString Override datalayout in the llvm assembly. +/// +/// parseAssemblyFileWithIndex is a wrapper around this function. +ParsedModuleAndIndex parseAssemblyWithIndex(MemoryBufferRef F, + SMDiagnostic &Err, + LLVMContext &Context, + SlotMapping *Slots = nullptr, + bool UpgradeDebugInfo = true, + StringRef DataLayoutString = ""); + +/// Parse LLVM Assembly for summary index from a MemoryBuffer. +/// +/// \param F The MemoryBuffer containing assembly with summary +/// \param Err Error result info. +/// +/// parseSummaryIndexAssemblyFile is a wrapper around this function. +std::unique_ptr +parseSummaryIndexAssembly(MemoryBufferRef F, SMDiagnostic &Err); + /// This function is the low-level interface to the LLVM Assembly Parser. /// This is kept as an independent function instead of being inlined into /// parseAssembly for the convenience of interactive users that want to add @@ -90,6 +159,7 @@ /// /// \param F The MemoryBuffer containing assembly /// \param M The module to add data to. +/// \param Index The index to add data to. /// \param Err Error result info. /// \param Slots The optional slot mapping that will be initialized during /// parsing. @@ -98,8 +168,8 @@ /// This option should only be set to false by llvm-as /// for use inside the LLVM testuite! /// \param DataLayoutString Override datalayout in the llvm assembly. -bool parseAssemblyInto(MemoryBufferRef F, Module &M, SMDiagnostic &Err, - SlotMapping *Slots = nullptr, +bool parseAssemblyInto(MemoryBufferRef F, Module *M, ModuleSummaryIndex *Index, + SMDiagnostic &Err, SlotMapping *Slots = nullptr, bool UpgradeDebugInfo = true, StringRef DataLayoutString = ""); Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -373,6 +373,8 @@ void setAliasee(GlobalValueSummary *Aliasee) { AliaseeSummary = Aliasee; } void setAliaseeGUID(GlobalValue::GUID GUID) { AliaseeGUID = GUID; } + bool hasAliasee() const { return !!AliaseeSummary; } + const GlobalValueSummary &getAliasee() const { assert(AliaseeSummary && "Unexpected missing aliasee summary"); return *AliaseeSummary; Index: lib/AsmParser/LLLexer.h =================================================================== --- lib/AsmParser/LLLexer.h +++ lib/AsmParser/LLLexer.h @@ -42,6 +42,10 @@ APFloat APFloatVal; APSInt APSIntVal; + // When false (default), an identifier ending in ':' is a label token. + // When true, the ':' is treated as a separate token. + bool IgnoreColonInIdentifiers; + public: explicit LLLexer(StringRef StartBuf, SourceMgr &SM, SMDiagnostic &, LLVMContext &C); @@ -59,6 +63,9 @@ const APSInt &getAPSIntVal() const { return APSIntVal; } const APFloat &getAPFloatVal() const { return APFloatVal; } + void setIgnoreColonInIdentifiers(bool val) { + IgnoreColonInIdentifiers = val; + } bool Error(LocTy L, const Twine &Msg) const; bool Error(const Twine &Msg) const { return Error(getLoc(), Msg); } Index: lib/AsmParser/LLLexer.cpp =================================================================== --- lib/AsmParser/LLLexer.cpp +++ lib/AsmParser/LLLexer.cpp @@ -159,7 +159,8 @@ LLLexer::LLLexer(StringRef StartBuf, SourceMgr &sm, SMDiagnostic &Err, LLVMContext &C) - : CurBuf(StartBuf), ErrorInfo(Err), SM(sm), Context(C), APFloatVal(0.0) { + : CurBuf(StartBuf), ErrorInfo(Err), SM(sm), Context(C), APFloatVal(0.0), + IgnoreColonInIdentifiers(false) { CurPtr = CurBuf.begin(); } @@ -221,6 +222,8 @@ case '!': return LexExclaim(); case '^': return LexCaret(); + case ':': + return lltok::colon; case '#': return LexHash(); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': @@ -461,8 +464,9 @@ KeywordEnd = CurPtr; } - // If we stopped due to a colon, this really is a label. - if (*CurPtr == ':') { + // If we stopped due to a colon, unless we were directed to ignore it, + // this really is a label. + if (!IgnoreColonInIdentifiers && *CurPtr == ':') { StrVal.assign(StartChar-1, CurPtr++); return lltok::LabelStr; } @@ -715,6 +719,73 @@ KEYWORD(catch); KEYWORD(filter); + // Summary index keywords. + KEYWORD(path); + KEYWORD(hash); + KEYWORD(gv); + KEYWORD(guid); + KEYWORD(name); + KEYWORD(summaries); + KEYWORD(flags); + KEYWORD(linkage); + KEYWORD(notEligibleToImport); + KEYWORD(live); + KEYWORD(dsoLocal); + KEYWORD(function); + KEYWORD(insts); + KEYWORD(funcFlags); + KEYWORD(readNone); + KEYWORD(readOnly); + KEYWORD(noRecurse); + KEYWORD(returnDoesNotAlias); + KEYWORD(calls); + KEYWORD(callee); + KEYWORD(hotness); + KEYWORD(unknown); + KEYWORD(hot); + KEYWORD(critical); + KEYWORD(relbf); + KEYWORD(variable); + KEYWORD(aliasee); + KEYWORD(refs); + KEYWORD(typeIdInfo); + KEYWORD(typeTests); + KEYWORD(typeTestAssumeVCalls); + KEYWORD(typeCheckedLoadVCalls); + KEYWORD(typeTestAssumeConstVCalls); + KEYWORD(typeCheckedLoadConstVCalls); + KEYWORD(vFuncId); + KEYWORD(offset); + KEYWORD(args); + KEYWORD(typeid); + KEYWORD(summary); + KEYWORD(typeTestRes); + KEYWORD(kind); + KEYWORD(unsat); + KEYWORD(byteArray); + KEYWORD(inline); + KEYWORD(single); + KEYWORD(allOnes); + KEYWORD(sizeM1BitWidth); + KEYWORD(alignLog2); + KEYWORD(sizeM1); + KEYWORD(bitMask); + KEYWORD(inlineBits); + KEYWORD(wpdResolutions); + KEYWORD(wpdRes); + KEYWORD(indir); + KEYWORD(singleImpl); + KEYWORD(branchFunnel); + KEYWORD(singleImplName); + KEYWORD(resByArg); + KEYWORD(byArg); + KEYWORD(uniformRetVal); + KEYWORD(uniqueRetVal); + KEYWORD(virtualConstProp); + KEYWORD(info); + KEYWORD(byte); + KEYWORD(bit); + #undef KEYWORD // Keywords for types. Index: lib/AsmParser/LLParser.h =================================================================== --- lib/AsmParser/LLParser.h +++ lib/AsmParser/LLParser.h @@ -20,6 +20,7 @@ #include "llvm/IR/Attributes.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Operator.h" #include "llvm/IR/Type.h" #include "llvm/IR/ValueHandle.h" @@ -90,7 +91,10 @@ private: LLVMContext &Context; LLLexer Lex; + // Module being parsed, null if we are only parsing summary index. Module *M; + // Summary index being parsed, null if we are only parsing Module. + ModuleSummaryIndex *Index; SlotMapping *Slots; // Instruction metadata resolution. Each instruction can have a list of @@ -139,6 +143,20 @@ std::map > ForwardRefAttrGroups; std::map NumberedAttrBuilders; + // Summary global value reference information. + std::map>> + ForwardRefValueInfos; + std::map>> + ForwardRefAliasees; + std::vector NumberedValueInfos; + + // Summary type id reference information. + std::map>> + ForwardRefTypeIds; + + // Map of module ID to path. + std::map ModuleIdMap; + /// Only the llvm-as tool may set this to false to bypass /// UpgradeDebuginfo so it can generate broken bitcode. bool UpgradeDebugInfo; @@ -146,11 +164,14 @@ /// DataLayout string to override that in LLVM assembly. StringRef DataLayoutStr; + std::string SourceFileName; + public: LLParser(StringRef F, SourceMgr &SM, SMDiagnostic &Err, Module *M, + ModuleSummaryIndex *Index, LLVMContext &Context, SlotMapping *Slots = nullptr, bool UpgradeDebugInfo = true, StringRef DataLayoutString = "") - : Context(M->getContext()), Lex(F, SM, Err, M->getContext()), M(M), + : Context(Context), Lex(F, SM, Err, Context), M(M), Index(Index), Slots(Slots), BlockAddressPFS(nullptr), UpgradeDebugInfo(UpgradeDebugInfo), DataLayoutStr(DataLayoutString) { if (!DataLayoutStr.empty()) @@ -239,6 +260,7 @@ Loc = Lex.getLoc(); return ParseUInt64(Val); } + bool ParseFlag(unsigned &Val); bool ParseStringAttribute(AttrBuilder &B); @@ -281,6 +303,7 @@ // Top-Level Entities bool ParseTopLevelEntities(); bool ValidateEndOfModule(); + bool ValidateEndOfIndex(); bool ParseTargetDefinition(); bool ParseModuleAsm(); bool ParseSourceFileName(); @@ -312,8 +335,48 @@ bool ParseFnAttributeValuePairs(AttrBuilder &B, std::vector &FwdRefAttrGrps, bool inAttrGrp, LocTy &BuiltinLoc); + + // Module Summary Index Parsing. bool SkipModuleSummaryEntry(); bool ParseSummaryEntry(); + bool ParseModuleEntry(unsigned ID); + bool ParseModuleReference(StringRef &ModulePath); + bool ParseGVReference(ValueInfo &VI, unsigned &GVId); + bool ParseGVEntry(unsigned ID); + bool ParseFunctionSummary(std::string Name, GlobalValue::GUID, unsigned ID); + bool ParseVariableSummary(std::string Name, GlobalValue::GUID, unsigned ID); + bool ParseAliasSummary(std::string Name, GlobalValue::GUID, unsigned ID); + bool ParseGVFlags(GlobalValueSummary::GVFlags &GVFlags); + bool ParseOptionalFFlags(FunctionSummary::FFlags &FFlags); + bool ParseOptionalCalls(std::vector &Calls); + bool ParseHotness(CalleeInfo::HotnessType &Hotness); + bool ParseOptionalTypeIdInfo(FunctionSummary::TypeIdInfo &TypeIdInfo); + bool ParseTypeTests(std::vector &TypeTests); + bool ParseVFuncIdList(lltok::Kind Kind, + std::vector &VFuncIdList); + bool ParseConstVCallList( + lltok::Kind Kind, + std::vector &ConstVCallList); + using IdToIndexMapType = + std::map>>; + bool ParseConstVCall(FunctionSummary::ConstVCall &ConstVCall, + IdToIndexMapType &IdToIndexMap, unsigned Index); + bool ParseVFuncId(FunctionSummary::VFuncId &VFuncId, + IdToIndexMapType &IdToIndexMap, unsigned Index); + bool ParseOptionalRefs(std::vector &Refs); + bool ParseTypeIdEntry(unsigned ID); + bool ParseTypeIdSummary(TypeIdSummary &TIS); + bool ParseTypeTestResolution(TypeTestResolution &TTRes); + bool ParseOptionalWpdResolutions( + std::map &WPDResMap); + bool ParseWpdRes(WholeProgramDevirtResolution &WPDRes); + bool ParseOptionalResByArg( + std::map, WholeProgramDevirtResolution::ByArg> + &ResByArg); + bool ParseArgs(std::vector &Args); + void AddGlobalValueToIndex(std::string Name, GlobalValue::GUID, + GlobalValue::LinkageTypes Linkage, unsigned ID, + std::unique_ptr Summary); // Type Parsing. bool ParseType(Type *&Result, const Twine &Msg, bool AllowVoid = false); Index: lib/AsmParser/LLParser.cpp =================================================================== --- lib/AsmParser/LLParser.cpp +++ lib/AsmParser/LLParser.cpp @@ -71,8 +71,8 @@ Lex.getLoc(), "Can't read textual IR with a Context that discards named Values"); - return ParseTopLevelEntities() || - ValidateEndOfModule(); + return ParseTopLevelEntities() || ValidateEndOfModule() || + ValidateEndOfIndex(); } bool LLParser::parseStandaloneConstantValue(Constant *&C, @@ -120,6 +120,8 @@ /// ValidateEndOfModule - Do final validity and sanity checks at the end of the /// module. bool LLParser::ValidateEndOfModule() { + if (!M) + return false; // Handle any function attribute group forward references. for (const auto &RAG : ForwardRefAttrGroups) { Value *V = RAG.first; @@ -258,11 +260,54 @@ return false; } +/// Do final validity and sanity checks at the end of the index. +bool LLParser::ValidateEndOfIndex() { + if (!Index) + return false; + + if (!ForwardRefValueInfos.empty()) + return Error(ForwardRefValueInfos.begin()->second.front().second, + "use of undefined summary '^" + + Twine(ForwardRefValueInfos.begin()->first) + "'"); + + if (!ForwardRefAliasees.empty()) + return Error(ForwardRefAliasees.begin()->second.front().second, + "use of undefined summary '^" + + Twine(ForwardRefAliasees.begin()->first) + "'"); + + if (!ForwardRefTypeIds.empty()) + return Error(ForwardRefTypeIds.begin()->second.front().second, + "use of undefined type id summary '^" + + Twine(ForwardRefTypeIds.begin()->first) + "'"); + + return false; +} + //===----------------------------------------------------------------------===// // Top-Level Entities //===----------------------------------------------------------------------===// bool LLParser::ParseTopLevelEntities() { + // If there is no Module, then parse just the summary index entries. + if (!M) { + while (true) { + switch (Lex.getKind()) { + case lltok::Eof: + return false; + case lltok::SummaryID: + if (ParseSummaryEntry()) + return true; + break; + case lltok::kw_source_filename: + if (ParseSourceFileName()) + return true; + break; + default: + // Skip everything else + Lex.Lex(); + } + } + } while (true) { switch (Lex.getKind()) { default: return TokError("expected top-level entity"); @@ -341,12 +386,12 @@ /// ::= 'source_filename' '=' STRINGCONSTANT bool LLParser::ParseSourceFileName() { assert(Lex.getKind() == lltok::kw_source_filename); - std::string Str; Lex.Lex(); if (ParseToken(lltok::equal, "expected '=' after source_filename") || - ParseStringConstant(Str)) + ParseStringConstant(SourceFileName)) return true; - M->setSourceFileName(Str); + if (M) + M->setSourceFileName(SourceFileName); return false; } @@ -721,8 +766,12 @@ // 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") || + if (Lex.getKind() != lltok::kw_gv && Lex.getKind() != lltok::kw_module && + Lex.getKind() != lltok::kw_typeid) + return TokError( + "Expected 'gv', 'module', or 'typeid' at the start of summary entry"); + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':' 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 @@ -747,20 +796,36 @@ return false; } -/// ParseSummaryEntry: -/// ::= SummaryID '=' ... +/// SummaryEntry +/// ::= SummaryID '=' GVEntry | ModuleEntry | TypeIdEntry bool LLParser::ParseSummaryEntry() { assert(Lex.getKind() == lltok::SummaryID); - // unsigned SummaryID = Lex.getUIntVal(); + unsigned SummaryID = Lex.getUIntVal(); + + // For summary entries, colons should be treated as distinct tokens, + // not an indication of the end of a label token. + Lex.setIgnoreColonInIdentifiers(true); 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; + // If we don't have an index object, skip the summary entry. + if (!Index) + return SkipModuleSummaryEntry(); + + switch (Lex.getKind()) { + case lltok::kw_gv: + return ParseGVEntry(SummaryID); + case lltok::kw_module: + return ParseModuleEntry(SummaryID); + case lltok::kw_typeid: + return ParseTypeIdEntry(SummaryID); + break; + default: + return Error(Lex.getLoc(), "unexpected summary kind"); + } + Lex.setIgnoreColonInIdentifiers(false); return false; } @@ -6922,3 +6987,1157 @@ return sortUseListOrder(V, Indexes, Loc); } + +/// ModuleEntry +/// ::= 'module' ':' '(' 'path' ':' STRINGCONSTANT ',' 'hash' ':' Hash ')' +/// Hash ::= '(' UInt32 ',' UInt32 ',' UInt32 ',' UInt32 ',' UInt32 ')' +bool LLParser::ParseModuleEntry(unsigned ID) { + assert(Lex.getKind() == lltok::kw_module); + Lex.Lex(); + + std::string Path; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_path, "expected 'path' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseStringConstant(Path) || + ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_hash, "expected 'hash' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + ModuleHash Hash; + if (ParseUInt32(Hash[0]) || ParseToken(lltok::comma, "expected ',' here") || + ParseUInt32(Hash[1]) || ParseToken(lltok::comma, "expected ',' here") || + ParseUInt32(Hash[2]) || ParseToken(lltok::comma, "expected ',' here") || + ParseUInt32(Hash[3]) || ParseToken(lltok::comma, "expected ',' here") || + ParseUInt32(Hash[4])) + return true; + + if (ParseToken(lltok::rparen, "expected ')' here") || + ParseToken(lltok::rparen, "expected ')' here")) + return true; + + auto ModuleEntry = Index->addModule(Path, ID, Hash); + ModuleIdMap[ID] = ModuleEntry->first(); + + return false; +} + +/// TypeIdEntry +/// ::= 'typeid' ':' '(' 'name' ':' STRINGCONSTANT ',' TypeIdSummary ')' +bool LLParser::ParseTypeIdEntry(unsigned ID) { + assert(Lex.getKind() == lltok::kw_typeid); + Lex.Lex(); + + std::string Name; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_name, "expected 'name' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseStringConstant(Name)) + return true; + + TypeIdSummary &TIS = Index->getOrInsertTypeIdSummary(Name); + if (ParseToken(lltok::comma, "expected ',' here") || + ParseTypeIdSummary(TIS) || ParseToken(lltok::rparen, "expected ')' here")) + return true; + + // Check if this ID was forward referenced, and if so, update the + // corresponding GUIDs. + auto FwdRefTIDs = ForwardRefTypeIds.find(ID); + if (FwdRefTIDs != ForwardRefTypeIds.end()) { + for (auto TIDRef : FwdRefTIDs->second) { + assert(!*TIDRef.first && + "Forward referenced type id GUID expected to be 0"); + *TIDRef.first = GlobalValue::getGUID(Name); + } + ForwardRefTypeIds.erase(FwdRefTIDs); + } + + return false; +} + +/// TypeIdSummary +/// ::= 'summary' ':' '(' TypeTestResolution [',' OptionalWpdResolutions]? ')' +bool LLParser::ParseTypeIdSummary(TypeIdSummary &TIS) { + if (ParseToken(lltok::kw_summary, "expected 'summary' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseTypeTestResolution(TIS.TTRes)) + return true; + + if (EatIfPresent(lltok::comma)) { + // Expect optional wpdResolutions field + if (ParseOptionalWpdResolutions(TIS.WPDRes)) + return true; + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// TypeTestResolution +/// ::= 'typeTestRes' ':' '(' 'kind' ':' +/// ( 'unsat' | 'byteArray' | 'inline' | 'single' | 'allOnes' ) ',' +/// 'sizeM1BitWidth' ':' SizeM1BitWidth [',' 'alignLog2' ':' UInt64]? +/// [',' 'sizeM1' ':' UInt64]? [',' 'bitMask' ':' UInt8]? +/// [',' 'inlinesBits' ':' UInt64]? ')' +bool LLParser::ParseTypeTestResolution(TypeTestResolution &TTRes) { + if (ParseToken(lltok::kw_typeTestRes, "expected 'typeTestRes' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_kind, "expected 'kind' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + switch (Lex.getKind()) { + case lltok::kw_unsat: + TTRes.TheKind = TypeTestResolution::Unsat; + break; + case lltok::kw_byteArray: + TTRes.TheKind = TypeTestResolution::ByteArray; + break; + case lltok::kw_inline: + TTRes.TheKind = TypeTestResolution::Inline; + break; + case lltok::kw_single: + TTRes.TheKind = TypeTestResolution::Single; + break; + case lltok::kw_allOnes: + TTRes.TheKind = TypeTestResolution::AllOnes; + break; + default: + return Error(Lex.getLoc(), "unexpected TypeTestResolution kind"); + } + Lex.Lex(); + + if (ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_sizeM1BitWidth, "expected 'sizeM1BitWidth' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseUInt32(TTRes.SizeM1BitWidth)) + return true; + + // Parse optional fields + while (EatIfPresent(lltok::comma)) { + switch (Lex.getKind()) { + case lltok::kw_alignLog2: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || + ParseUInt64(TTRes.AlignLog2)) + return true; + break; + case lltok::kw_sizeM1: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseUInt64(TTRes.SizeM1)) + return true; + break; + case lltok::kw_bitMask: { + unsigned Val; + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseUInt32(Val)) + return true; + assert(Val <= 0xff); + TTRes.BitMask = (uint8_t)Val; + break; + } + case lltok::kw_inlineBits: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || + ParseUInt64(TTRes.InlineBits)) + return true; + break; + default: + return Error(Lex.getLoc(), "expected optional TypeTestResolution field"); + } + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// OptionalWpdResolutions +/// ::= 'wpsResolutions' ':' '(' WpdResolution [',' WpdResolution]* ')' +/// WpdResolution ::= '(' 'offset' ':' UInt64 ',' WpdRes ')' +bool LLParser::ParseOptionalWpdResolutions( + std::map &WPDResMap) { + if (ParseToken(lltok::kw_wpdResolutions, "expected 'wpdResolutions' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + do { + uint64_t Offset; + WholeProgramDevirtResolution WPDRes; + if (ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_offset, "expected 'offset' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseUInt64(Offset) || + ParseToken(lltok::comma, "expected ',' here") || ParseWpdRes(WPDRes) || + ParseToken(lltok::rparen, "expected ')' here")) + return true; + WPDResMap[Offset] = WPDRes; + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// WpdRes +/// ::= 'wpdRes' ':' '(' 'kind' ':' 'indir' +/// [',' OptionalResByArg]? ')' +/// ::= 'wpdRes' ':' '(' 'kind' ':' 'singleImpl' +/// ',' 'singleImplName' ':' STRINGCONSTANT ',' +/// [',' OptionalResByArg]? ')' +/// ::= 'wpdRes' ':' '(' 'kind' ':' 'branchFunnel' +/// [',' OptionalResByArg]? ')' +bool LLParser::ParseWpdRes(WholeProgramDevirtResolution &WPDRes) { + if (ParseToken(lltok::kw_wpdRes, "expected 'wpdRes' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_kind, "expected 'kind' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + switch (Lex.getKind()) { + case lltok::kw_indir: + WPDRes.TheKind = WholeProgramDevirtResolution::Indir; + break; + case lltok::kw_singleImpl: + WPDRes.TheKind = WholeProgramDevirtResolution::SingleImpl; + break; + case lltok::kw_branchFunnel: + WPDRes.TheKind = WholeProgramDevirtResolution::BranchFunnel; + break; + default: + return Error(Lex.getLoc(), "unexpected WholeProgramDevirtResolution kind"); + } + Lex.Lex(); + + // Parse optional fields + while (EatIfPresent(lltok::comma)) { + switch (Lex.getKind()) { + case lltok::kw_singleImplName: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':' here") || + ParseStringConstant(WPDRes.SingleImplName)) + return true; + break; + case lltok::kw_resByArg: + if (ParseOptionalResByArg(WPDRes.ResByArg)) + return true; + default: + return Error(Lex.getLoc(), + "expected optional WholeProgramDevirtResolution field"); + } + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// OptionalResByArg +/// ::= 'wpdRes' ':' '(' ResByArg[, ResByArg]* ')' +/// ResByArg ::= Args ',' 'byArg' ':' '(' 'kind' ':' +/// ( 'indir' | 'uniformRetVal' | 'UniqueRetVal' | +/// 'virtualConstProp' ) +/// [',' 'info' ':' UInt64]? [',' 'byte' ':' UInt32]? +/// [',' 'bit' ':' UInt32]? ')' +bool LLParser::ParseOptionalResByArg( + std::map, WholeProgramDevirtResolution::ByArg> + &ResByArg) { + if (ParseToken(lltok::kw_wpdRes, "expected 'wpdRes' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + do { + std::vector Args; + if (ParseArgs(Args) || ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_byArg, "expected 'byArg here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_kind, "expected 'kind' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + WholeProgramDevirtResolution::ByArg ByArg; + switch (Lex.getKind()) { + case lltok::kw_indir: + ByArg.TheKind = WholeProgramDevirtResolution::ByArg::Indir; + break; + case lltok::kw_uniformRetVal: + ByArg.TheKind = WholeProgramDevirtResolution::ByArg::UniformRetVal; + break; + case lltok::kw_uniqueRetVal: + ByArg.TheKind = WholeProgramDevirtResolution::ByArg::UniqueRetVal; + break; + case lltok::kw_virtualConstProp: + ByArg.TheKind = WholeProgramDevirtResolution::ByArg::VirtualConstProp; + break; + default: + return Error(Lex.getLoc(), + "unexpected WholeProgramDevirtResolution::ByArg kind"); + } + Lex.Lex(); + + // Parse optional fields + while (EatIfPresent(lltok::comma)) { + switch (Lex.getKind()) { + case lltok::kw_info: + if (ParseToken(lltok::colon, "expected ':' here") || + ParseUInt64(ByArg.Info)) + return true; + case lltok::kw_byte: + if (ParseToken(lltok::colon, "expected ':' here") || + ParseUInt32(ByArg.Byte)) + return true; + case lltok::kw_bit: + if (ParseToken(lltok::colon, "expected ':' here") || + ParseUInt32(ByArg.Bit)) + return true; + default: + return Error(Lex.getLoc(), + "expected optional whole program devirt field"); + } + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + ResByArg[Args] = ByArg; + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// OptionalResByArg +/// ::= 'args' ':' '(' UInt64[, UInt64]* ')' +bool LLParser::ParseArgs(std::vector &Args) { + if (ParseToken(lltok::kw_args, "expected 'args' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + do { + uint64_t Val; + if (ParseUInt64(Val)) + return true; + Args.push_back(Val); + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +static ValueInfo EmptyVI = + ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-8); + +/// Stores the given Name/GUID and associated summary into the Index. +/// Also updates any forward references to the associated entry ID. +void LLParser::AddGlobalValueToIndex( + std::string Name, GlobalValue::GUID GUID, GlobalValue::LinkageTypes Linkage, + unsigned ID, std::unique_ptr Summary) { + // First create the ValueInfo utilizing the Name or GUID. + ValueInfo VI; + if (GUID != 0) { + assert(Name.empty()); + VI = Index->getOrInsertValueInfo(GUID); + } else { + assert(!Name.empty()); + if (M) { + auto *GV = M->getNamedValue(Name); + assert(GV); + VI = Index->getOrInsertValueInfo(GV); + } else { + assert( + (!GlobalValue::isLocalLinkage(Linkage) || !SourceFileName.empty()) && + "Need a source_filename to compute GUID for local"); + GUID = GlobalValue::getGUID( + GlobalValue::getGlobalIdentifier(Name, Linkage, SourceFileName)); + VI = Index->getOrInsertValueInfo(GUID, Index->saveString(Name)); + } + } + + // Add the summary if one was provided. + if (Summary) + Index->addGlobalValueSummary(VI, std::move(Summary)); + + // Resolve forward references from calls/refs + auto FwdRefVIs = ForwardRefValueInfos.find(ID); + if (FwdRefVIs != ForwardRefValueInfos.end()) { + for (auto VIRef : FwdRefVIs->second) { + assert(*VIRef.first == EmptyVI && + "Forward referenced ValueInfo expected to be empty"); + *VIRef.first = VI; + } + ForwardRefValueInfos.erase(FwdRefVIs); + } + + // Resolve forward references from aliases + auto FwdRefAliasees = ForwardRefAliasees.find(ID); + if (FwdRefAliasees != ForwardRefAliasees.end()) { + for (auto AliaseeRef : FwdRefAliasees->second) { + assert(!AliaseeRef.first->hasAliasee() && + "Forward referencing alias already has aliasee"); + AliaseeRef.first->setAliasee(VI.getSummaryList().front().get()); + } + ForwardRefAliasees.erase(FwdRefAliasees); + } + + // Save the associated ValueInfo for use in later references by ID. + if (ID == NumberedValueInfos.size()) + NumberedValueInfos.push_back(VI); + else { + // Handle non-continuous numbers (to make test simplification easier). + if (ID > NumberedValueInfos.size()) + NumberedValueInfos.resize(ID + 1); + NumberedValueInfos[ID] = VI; + } +} + +/// ParseGVEntry +/// ::= 'gv' ':' '(' ('name' ':' STRINGCONSTANT | 'guid' ':' UInt64) +/// [',' 'summaries' ':' Summary[',' Summary]* ]? ')' +/// Summary ::= '(' (FunctionSummary | VariableSummary | AliasSummary) ')' +bool LLParser::ParseGVEntry(unsigned ID) { + assert(Lex.getKind() == lltok::kw_gv); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + std::string Name; + GlobalValue::GUID GUID = 0; + switch (Lex.getKind()) { + case lltok::kw_name: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':' here") || + ParseStringConstant(Name)) + return true; + // Can't create GUID/ValueInfo until we have the linkage. + break; + case lltok::kw_guid: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':' here") || ParseUInt64(GUID)) + return true; + break; + default: + return Error(Lex.getLoc(), "expected name or guid tag"); + } + + if (!EatIfPresent(lltok::comma)) { + // No summaries. Wrap up. + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + // This was created for a call to an external or indirect target. + // A GUID with no summary came from a VALUE_GUID record, dummy GUID + // created for indirect calls with VP. A Name with no GUID came from + // an external definition. We pass ExternalLinkage since that is only + // used when the GUID must be computed from Name, and in that case + // the symbol must have external linkage. + AddGlobalValueToIndex(Name, GUID, GlobalValue::ExternalLinkage, ID, + nullptr); + return false; + } + + // Have a list of summaries + if (ParseToken(lltok::kw_summaries, "expected 'summaries' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + do { + if (ParseToken(lltok::lparen, "expected '(' here")) + return true; + switch (Lex.getKind()) { + case lltok::kw_function: + if (ParseFunctionSummary(Name, GUID, ID)) + return true; + break; + case lltok::kw_variable: + if (ParseVariableSummary(Name, GUID, ID)) + return true; + break; + case lltok::kw_alias: + if (ParseAliasSummary(Name, GUID, ID)) + return true; + break; + default: + return Error(Lex.getLoc(), "expected summary type"); + } + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// FunctionSummary +/// ::= 'function' ':' '(' 'module' ':' ModuleReference ',' GVFlags +/// ',' 'insts' ':' UInt32 [',' OptionalFFlags]? [',' OptionalCalls]? +/// [',' OptionalTypeIdInfo]? [',' OptionalRefs]? ')' +bool LLParser::ParseFunctionSummary(std::string Name, GlobalValue::GUID GUID, + unsigned ID) { + assert(Lex.getKind() == lltok::kw_function); + Lex.Lex(); + + StringRef ModulePath; + GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( + /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, + /*Live=*/false, /*IsLocal=*/false); + unsigned InstCount; + std::vector Calls; + FunctionSummary::TypeIdInfo TypeIdInfo; + std::vector Refs; + // Default is all-zeros (conservative values). + FunctionSummary::FFlags FFlags = {}; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseModuleReference(ModulePath) || + ParseToken(lltok::comma, "expected ',' here") || ParseGVFlags(GVFlags) || + ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_insts, "expected 'insts' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseUInt32(InstCount)) + return true; + + // Parse optional fields + while (EatIfPresent(lltok::comma)) { + switch (Lex.getKind()) { + case lltok::kw_funcFlags: + if (ParseOptionalFFlags(FFlags)) + return true; + break; + case lltok::kw_calls: + if (ParseOptionalCalls(Calls)) + return true; + break; + case lltok::kw_typeIdInfo: + if (ParseOptionalTypeIdInfo(TypeIdInfo)) + return true; + break; + case lltok::kw_refs: + if (ParseOptionalRefs(Refs)) + return true; + break; + default: + return Error(Lex.getLoc(), "expected optional function summary field"); + } + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + auto FS = llvm::make_unique( + GVFlags, InstCount, FFlags, std::move(Refs), std::move(Calls), + std::move(TypeIdInfo.TypeTests), + std::move(TypeIdInfo.TypeTestAssumeVCalls), + std::move(TypeIdInfo.TypeCheckedLoadVCalls), + std::move(TypeIdInfo.TypeTestAssumeConstVCalls), + std::move(TypeIdInfo.TypeCheckedLoadConstVCalls)); + + FS->setModulePath(ModulePath); + + AddGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage, + ID, std::move(FS)); + + return false; +} + +/// VariableSummary +/// ::= 'variable' ':' '(' 'module' ':' ModuleReference ',' GVFlags +/// [',' OptionalRefs]? ')' +bool LLParser::ParseVariableSummary(std::string Name, GlobalValue::GUID GUID, + unsigned ID) { + assert(Lex.getKind() == lltok::kw_variable); + Lex.Lex(); + + StringRef ModulePath; + GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( + /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, + /*Live=*/false, /*IsLocal=*/false); + std::vector Refs; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseModuleReference(ModulePath) || + ParseToken(lltok::comma, "expected ',' here") || ParseGVFlags(GVFlags)) + return true; + + // Parse optional refs field + if (EatIfPresent(lltok::comma)) { + if (ParseOptionalRefs(Refs)) + return true; + } + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + auto GS = llvm::make_unique(GVFlags, std::move(Refs)); + + GS->setModulePath(ModulePath); + + AddGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage, + ID, std::move(GS)); + + return false; +} + +/// AliasSummary +/// ::= 'alias' ':' '(' 'module' ':' ModuleReference ',' GVFlags ',' +/// 'aliasee' ':' GVReference ')' +bool LLParser::ParseAliasSummary(std::string Name, GlobalValue::GUID GUID, + unsigned ID) { + assert(Lex.getKind() == lltok::kw_alias); + LocTy Loc = Lex.getLoc(); + Lex.Lex(); + + StringRef ModulePath; + GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( + /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, + /*Live=*/false, /*IsLocal=*/false); + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseModuleReference(ModulePath) || + ParseToken(lltok::comma, "expected ',' here") || ParseGVFlags(GVFlags) || + ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_aliasee, "expected 'aliasee' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + ValueInfo AliaseeVI; + unsigned GVId; + if (ParseGVReference(AliaseeVI, GVId)) + return true; + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + auto AS = llvm::make_unique(GVFlags); + + AS->setModulePath(ModulePath); + + // Record forward reference if the aliasee is not parsed yet. + if (AliaseeVI == EmptyVI) { + auto FwdRef = ForwardRefAliasees.insert( + std::make_pair(GVId, std::vector>())); + FwdRef.first->second.push_back(std::make_pair(AS.get(), Loc)); + } + + AddGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage, + ID, std::move(AS)); + + return false; +} + +/// Flag +/// ::= [0|1] +bool LLParser::ParseFlag(unsigned &Val) { + if (Lex.getKind() != lltok::APSInt || Lex.getAPSIntVal().isSigned()) + return TokError("expected integer"); + Val = (unsigned)Lex.getAPSIntVal().getBoolValue(); + Lex.Lex(); + return false; +} + +/// OptionalFFlags +/// := 'funcFlags' ':' '(' ['readNone' ':' Flag]? +/// [',' 'readOnly' ':' Flag]? [',' 'noRecurse' ':' Flag]? +/// [',' 'returnDoesNotAlias' ':' Flag]? ')' +bool LLParser::ParseOptionalFFlags(FunctionSummary::FFlags &FFlags) { + assert(Lex.getKind() == lltok::kw_funcFlags); + Lex.Lex(); + + if (ParseToken(lltok::lparen, "expected '(' in funcFlags")) + return true; + + do { + unsigned Val; + switch (Lex.getKind()) { + case lltok::kw_readNone: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseFlag(Val)) + return true; + FFlags.ReadNone = Val; + case lltok::kw_readOnly: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseFlag(Val)) + return true; + FFlags.ReadOnly = Val; + case lltok::kw_noRecurse: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseFlag(Val)) + return true; + FFlags.NoRecurse = Val; + case lltok::kw_returnDoesNotAlias: + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'") || ParseFlag(Val)) + return true; + FFlags.ReturnDoesNotAlias = Val; + default: + return Error(Lex.getLoc(), "expected function flag type"); + } + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' in funcFlags")) + return true; + + return false; +} + +/// OptionalCalls +/// := 'calls' ':' '(' Call [',' Call]* ')' +/// Call ::= '(' 'callee' ':' GVReference +/// [( ',' 'hotness' ':' Hotness | ',' 'relbf' ':' UInt32 )]? ')' +bool LLParser::ParseOptionalCalls(std::vector &Calls) { + assert(Lex.getKind() == lltok::kw_calls); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' in calls") | + ParseToken(lltok::lparen, "expected '(' in calls")) + return true; + + IdToIndexMapType IdToIndexMap; + // Parse each call edge + do { + ValueInfo VI; + if (ParseToken(lltok::lparen, "expected '(' in call") || + ParseToken(lltok::kw_callee, "expected 'callee' in call") || + ParseToken(lltok::colon, "expected ':'")) + return true; + + LocTy Loc = Lex.getLoc(); + unsigned GVId; + if (ParseGVReference(VI, GVId)) + return true; + + CalleeInfo::HotnessType Hotness = CalleeInfo::HotnessType::Unknown; + unsigned RelBF = 0; + if (EatIfPresent(lltok::comma)) { + // Expect either hotness or relbf + if (EatIfPresent(lltok::kw_hotness)) { + if (ParseToken(lltok::colon, "expected ':'") || ParseHotness(Hotness)) + return true; + } else { + if (ParseToken(lltok::kw_relbf, "expected relbf") || + ParseToken(lltok::colon, "expected ':'") || ParseUInt32(RelBF)) + return true; + } + } + // Keep track of the Call array index needing a forward reference. + // We will save the location of the ValueInfo needing an update, but + // can only do so once the std::vector is finalized. + if (VI == EmptyVI) + IdToIndexMap[GVId].push_back(std::make_pair(Calls.size(), Loc)); + Calls.push_back(FunctionSummary::EdgeTy{VI, CalleeInfo(Hotness, RelBF)}); + + if (ParseToken(lltok::rparen, "expected ')' in call")) + return true; + } while (EatIfPresent(lltok::comma)); + + // Now that the Calls vector is finalized, it is safe to save the locations + // of any forward GV references that need updating later. + for (auto I : IdToIndexMap) { + for (auto P : I.second) { + assert(Calls[P.first].first == EmptyVI && + "Forward referenced ValueInfo expected to be empty"); + auto FwdRef = ForwardRefValueInfos.insert(std::make_pair( + I.first, std::vector>())); + FwdRef.first->second.push_back( + std::make_pair(&Calls[P.first].first, P.second)); + } + } + + if (ParseToken(lltok::rparen, "expected ')' in calls")) + return true; + + return false; +} + +/// Hotness +/// := ('unknown'|'cold'|'none'|'hot'|'critical') +bool LLParser::ParseHotness(CalleeInfo::HotnessType &Hotness) { + switch (Lex.getKind()) { + case lltok::kw_unknown: + Hotness = CalleeInfo::HotnessType::Unknown; + break; + case lltok::kw_cold: + Hotness = CalleeInfo::HotnessType::Cold; + break; + case lltok::kw_none: + Hotness = CalleeInfo::HotnessType::None; + break; + case lltok::kw_hot: + Hotness = CalleeInfo::HotnessType::Hot; + break; + case lltok::kw_critical: + Hotness = CalleeInfo::HotnessType::Critical; + break; + default: + return Error(Lex.getLoc(), "invalid call edge hotness"); + } + Lex.Lex(); + return false; +} + +/// OptionalRefs +/// := 'refs' ':' '(' GVReference [',' GVReference]* ')' +bool LLParser::ParseOptionalRefs(std::vector &Refs) { + assert(Lex.getKind() == lltok::kw_refs); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' in refs") | + ParseToken(lltok::lparen, "expected '(' in refs")) + return true; + + IdToIndexMapType IdToIndexMap; + // Parse each ref edge + do { + ValueInfo VI; + LocTy Loc = Lex.getLoc(); + unsigned GVId; + if (ParseGVReference(VI, GVId)) + return true; + + // Keep track of the Refs array index needing a forward reference. + // We will save the location of the ValueInfo needing an update, but + // can only do so once the std::vector is finalized. + if (VI == EmptyVI) + IdToIndexMap[GVId].push_back(std::make_pair(Refs.size(), Loc)); + Refs.push_back(VI); + } while (EatIfPresent(lltok::comma)); + + // Now that the Refs vector is finalized, it is safe to save the locations + // of any forward GV references that need updating later. + for (auto I : IdToIndexMap) { + for (auto P : I.second) { + assert(Refs[P.first] == EmptyVI && + "Forward referenced ValueInfo expected to be empty"); + auto FwdRef = ForwardRefValueInfos.insert(std::make_pair( + I.first, std::vector>())); + FwdRef.first->second.push_back(std::make_pair(&Refs[P.first], P.second)); + } + } + + if (ParseToken(lltok::rparen, "expected ')' in refs")) + return true; + + return false; +} + +/// OptionalTypeIdInfo +/// := 'typeidinfo' ':' '(' [',' TypeTests]? [',' TypeTestAssumeVCalls]? +/// [',' TypeCheckedLoadVCalls]? [',' TypeTestAssumeConstVCalls]? +/// [',' TypeCheckedLoadConstVCalls]? ')' +bool LLParser::ParseOptionalTypeIdInfo( + FunctionSummary::TypeIdInfo &TypeIdInfo) { + assert(Lex.getKind() == lltok::kw_typeIdInfo); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' in typeIdInfo")) + return true; + + do { + switch (Lex.getKind()) { + case lltok::kw_typeTests: + if (ParseTypeTests(TypeIdInfo.TypeTests)) + return true; + break; + case lltok::kw_typeTestAssumeVCalls: + if (ParseVFuncIdList(lltok::kw_typeTestAssumeVCalls, + TypeIdInfo.TypeTestAssumeVCalls)) + return true; + break; + case lltok::kw_typeCheckedLoadVCalls: + if (ParseVFuncIdList(lltok::kw_typeCheckedLoadVCalls, + TypeIdInfo.TypeCheckedLoadVCalls)) + return true; + break; + case lltok::kw_typeTestAssumeConstVCalls: + if (ParseConstVCallList(lltok::kw_typeTestAssumeConstVCalls, + TypeIdInfo.TypeTestAssumeConstVCalls)) + return true; + break; + case lltok::kw_typeCheckedLoadConstVCalls: + if (ParseConstVCallList(lltok::kw_typeCheckedLoadConstVCalls, + TypeIdInfo.TypeCheckedLoadConstVCalls)) + return true; + break; + default: + return Error(Lex.getLoc(), "invalid typeIdInfo list type"); + } + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' in typeIdInfo")) + return true; + + return false; +} + +/// TypeTests +/// ::= 'typeTests' ':' '(' (SummaryID | UInt64) +/// [',' (SummaryID | UInt64)]* ')' +bool LLParser::ParseTypeTests(std::vector &TypeTests) { + assert(Lex.getKind() == lltok::kw_typeTests); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' in typeIdInfo")) + return true; + + IdToIndexMapType IdToIndexMap; + do { + GlobalValue::GUID GUID = 0; + if (Lex.getKind() == lltok::SummaryID) { + unsigned ID = Lex.getUIntVal(); + LocTy Loc = Lex.getLoc(); + // Keep track of the TypeTests array index needing a forward reference. + // We will save the location of the GUID needing an update, but + // can only do so once the std::vector is finalized. + IdToIndexMap[ID].push_back(std::make_pair(TypeTests.size(), Loc)); + Lex.Lex(); + } else if (ParseUInt64(GUID)) + return true; + TypeTests.push_back(GUID); + } while (EatIfPresent(lltok::comma)); + + // Now that the TypeTests vector is finalized, it is safe to save the + // locations of any forward GV references that need updating later. + for (auto I : IdToIndexMap) { + for (auto P : I.second) { + assert(TypeTests[P.first] == 0 && + "Forward referenced type id GUID expected to be 0"); + auto FwdRef = ForwardRefTypeIds.insert(std::make_pair( + I.first, std::vector>())); + FwdRef.first->second.push_back( + std::make_pair(&TypeTests[P.first], P.second)); + } + } + + if (ParseToken(lltok::rparen, "expected ')' in typeIdInfo")) + return true; + + return false; +} + +/// VFuncIdList +/// ::= Kind ':' '(' VFuncId [',' VFuncId]* ')' +bool LLParser::ParseVFuncIdList( + lltok::Kind Kind, std::vector &VFuncIdList) { + assert(Lex.getKind() == Kind); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + IdToIndexMapType IdToIndexMap; + do { + FunctionSummary::VFuncId VFuncId; + if (ParseVFuncId(VFuncId, IdToIndexMap, VFuncIdList.size())) + return true; + VFuncIdList.push_back(VFuncId); + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + // Now that the VFuncIdList vector is finalized, it is safe to save the + // locations of any forward GV references that need updating later. + for (auto I : IdToIndexMap) { + for (auto P : I.second) { + assert(VFuncIdList[P.first].GUID == 0 && + "Forward referenced type id GUID expected to be 0"); + auto FwdRef = ForwardRefTypeIds.insert(std::make_pair( + I.first, std::vector>())); + FwdRef.first->second.push_back( + std::make_pair(&VFuncIdList[P.first].GUID, P.second)); + } + } + + return false; +} + +/// ConstVCallList +/// ::= Kind ':' '(' ConstVCall [',' ConstVCall]* ')' +bool LLParser::ParseConstVCallList( + lltok::Kind Kind, + std::vector &ConstVCallList) { + assert(Lex.getKind() == Kind); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + IdToIndexMapType IdToIndexMap; + do { + FunctionSummary::ConstVCall ConstVCall; + if (ParseConstVCall(ConstVCall, IdToIndexMap, ConstVCallList.size())) + return true; + ConstVCallList.push_back(ConstVCall); + } while (EatIfPresent(lltok::comma)); + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + // Now that the ConstVCallList vector is finalized, it is safe to save the + // locations of any forward GV references that need updating later. + for (auto I : IdToIndexMap) { + for (auto P : I.second) { + assert(ConstVCallList[P.first].VFunc.GUID == 0 && + "Forward referenced type id GUID expected to be 0"); + auto FwdRef = ForwardRefTypeIds.insert(std::make_pair( + I.first, std::vector>())); + FwdRef.first->second.push_back( + std::make_pair(&ConstVCallList[P.first].VFunc.GUID, P.second)); + } + } + + return false; +} + +/// ConstVCall +/// ::= VFuncId, Args +bool LLParser::ParseConstVCall(FunctionSummary::ConstVCall &ConstVCall, + IdToIndexMapType &IdToIndexMap, unsigned Index) { + if (ParseVFuncId(ConstVCall.VFunc, IdToIndexMap, Index) || + ParseToken(lltok::comma, "expected ',' here") || + ParseArgs(ConstVCall.Args)) + return true; + + return false; +} + +/// VFuncId +/// ::= 'vFuncId' ':' '(' (SummaryID | 'guid' ':' UInt64) ',' +/// 'offset' ':' UInt64 ')' +bool LLParser::ParseVFuncId(FunctionSummary::VFuncId &VFuncId, + IdToIndexMapType &IdToIndexMap, unsigned Index) { + assert(Lex.getKind() == lltok::kw_vFuncId); + Lex.Lex(); + + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here")) + return true; + + if (Lex.getKind() == lltok::SummaryID) { + VFuncId.GUID = 0; + unsigned ID = Lex.getUIntVal(); + LocTy Loc = Lex.getLoc(); + // Keep track of the array index needing a forward reference. + // We will save the location of the GUID needing an update, but + // can only do so once the caller's std::vector is finalized. + IdToIndexMap[ID].push_back(std::make_pair(Index, Loc)); + Lex.Lex(); + } else if (ParseToken(lltok::kw_guid, "expected 'guid' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseUInt64(VFuncId.GUID)) + return true; + + if (ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_offset, "expected 'offset' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseUInt64(VFuncId.Offset) || + ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// GVFlags +/// ::= 'flags' ':' '(' 'linkage' ':' OptionalLinkageAux ',' +/// 'notEligibleToImport' ':' Flag ',' 'live' ':' Flag ',' +/// 'dsoLocal' ':' Flag ')' +bool LLParser::ParseGVFlags(GlobalValueSummary::GVFlags &GVFlags) { + assert(Lex.getKind() == lltok::kw_flags); + Lex.Lex(); + + bool HasLinkage; + if (ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::lparen, "expected '(' here") || + ParseToken(lltok::kw_linkage, "expected 'linkage' here") || + ParseToken(lltok::colon, "expected ':' here")) + return true; + + GVFlags.Linkage = parseOptionalLinkageAux(Lex.getKind(), HasLinkage); + assert(HasLinkage && "Linkage not optional in summary entry"); + Lex.Lex(); + + unsigned Flag; + if (ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_notEligibleToImport, + "expected 'notEligibleToImport' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseFlag(Flag)) + return true; + GVFlags.NotEligibleToImport = Flag; + + if (ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_live, "expected 'live' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseFlag(Flag)) + return true; + GVFlags.Live = Flag; + + if (ParseToken(lltok::comma, "expected ',' here") || + ParseToken(lltok::kw_dsoLocal, "expected 'dsoLocal' here") || + ParseToken(lltok::colon, "expected ':' here") || ParseFlag(Flag)) + return true; + GVFlags.DSOLocal = Flag; + + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + + return false; +} + +/// ModuleReference +/// ::= 'module' ':' UInt +bool LLParser::ParseModuleReference(StringRef &ModulePath) { + // Parse module id. + if (ParseToken(lltok::kw_module, "expected 'module' here") || + ParseToken(lltok::colon, "expected ':' here") || + ParseToken(lltok::SummaryID, "expected module ID")) + return true; + + unsigned ModuleID = Lex.getUIntVal(); + auto I = ModuleIdMap.find(ModuleID); + // We should have already parsed all module IDs + assert(I != ModuleIdMap.end()); + ModulePath = I->second; + return false; +} + +/// GVReference +/// ::= SummaryID +bool LLParser::ParseGVReference(ValueInfo &VI, unsigned &GVId) { + if (ParseToken(lltok::SummaryID, "expected GV ID")) + return true; + + GVId = Lex.getUIntVal(); + + // Check if we already have a VI for this GV + if (GVId < NumberedValueInfos.size()) { + assert(NumberedValueInfos[GVId] != EmptyVI); + VI = NumberedValueInfos[GVId]; + } else + // We will create a forward reference to the stored location. + VI = EmptyVI; + + return false; +} Index: lib/AsmParser/LLToken.h =================================================================== --- lib/AsmParser/LLToken.h +++ lib/AsmParser/LLToken.h @@ -36,6 +36,7 @@ rparen, // ( ) exclaim, // ! bar, // | + colon, // : kw_x, kw_true, @@ -347,6 +348,73 @@ kw_uselistorder, kw_uselistorder_bb, + // Summary index keywords + kw_path, + kw_hash, + kw_gv, + kw_guid, + kw_name, + kw_summaries, + kw_flags, + kw_linkage, + kw_notEligibleToImport, + kw_live, + kw_dsoLocal, + kw_function, + kw_insts, + kw_funcFlags, + kw_readNone, + kw_readOnly, + kw_noRecurse, + kw_returnDoesNotAlias, + kw_calls, + kw_callee, + kw_hotness, + kw_unknown, + kw_hot, + kw_critical, + kw_relbf, + kw_variable, + kw_aliasee, + kw_refs, + kw_typeIdInfo, + kw_typeTests, + kw_typeTestAssumeVCalls, + kw_typeCheckedLoadVCalls, + kw_typeTestAssumeConstVCalls, + kw_typeCheckedLoadConstVCalls, + kw_vFuncId, + kw_offset, + kw_args, + kw_typeid, + kw_summary, + kw_typeTestRes, + kw_kind, + kw_unsat, + kw_byteArray, + kw_inline, + kw_single, + kw_allOnes, + kw_sizeM1BitWidth, + kw_alignLog2, + kw_sizeM1, + kw_bitMask, + kw_inlineBits, + kw_wpdResolutions, + kw_wpdRes, + kw_indir, + kw_singleImpl, + kw_branchFunnel, + kw_singleImplName, + kw_resByArg, + kw_byArg, + kw_uniformRetVal, + kw_uniqueRetVal, + kw_virtualConstProp, + kw_info, + kw_byte, + kw_bit, + // Unsigned Valued tokens (UIntVal). GlobalID, // @42 LocalVarID, // %42 Index: lib/AsmParser/Parser.cpp =================================================================== --- lib/AsmParser/Parser.cpp +++ lib/AsmParser/Parser.cpp @@ -15,6 +15,7 @@ #include "LLParser.h" #include "llvm/ADT/STLExtras.h" #include "llvm/IR/Module.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" @@ -22,14 +23,17 @@ #include using namespace llvm; -bool llvm::parseAssemblyInto(MemoryBufferRef F, Module &M, SMDiagnostic &Err, +bool llvm::parseAssemblyInto(MemoryBufferRef F, Module *M, + ModuleSummaryIndex *Index, SMDiagnostic &Err, SlotMapping *Slots, bool UpgradeDebugInfo, StringRef DataLayoutString) { SourceMgr SM; std::unique_ptr Buf = MemoryBuffer::getMemBuffer(F); SM.AddNewSourceBuffer(std::move(Buf), SMLoc()); - return LLParser(F.getBuffer(), SM, Err, &M, Slots, UpgradeDebugInfo, + LLVMContext Context; + return LLParser(F.getBuffer(), SM, Err, M, Index, + M ? M->getContext() : Context, Slots, UpgradeDebugInfo, DataLayoutString) .Run(); } @@ -41,7 +45,8 @@ std::unique_ptr M = make_unique(F.getBufferIdentifier(), Context); - if (parseAssemblyInto(F, *M, Err, Slots, UpgradeDebugInfo, DataLayoutString)) + if (parseAssemblyInto(F, M.get(), nullptr, Err, Slots, UpgradeDebugInfo, + DataLayoutString)) return nullptr; return M; @@ -63,6 +68,37 @@ UpgradeDebugInfo, DataLayoutString); } +ParsedModuleAndIndex llvm::parseAssemblyWithIndex( + MemoryBufferRef F, SMDiagnostic &Err, LLVMContext &Context, + SlotMapping *Slots, bool UpgradeDebugInfo, StringRef DataLayoutString) { + std::unique_ptr M = + make_unique(F.getBufferIdentifier(), Context); + std::unique_ptr Index = + make_unique(/*HaveGVs=*/true); + + if (parseAssemblyInto(F, M.get(), Index.get(), Err, Slots, UpgradeDebugInfo, + DataLayoutString)) + return {nullptr, nullptr}; + + return {std::move(M), std::move(Index)}; +} + +ParsedModuleAndIndex llvm::parseAssemblyFileWithIndex( + StringRef Filename, SMDiagnostic &Err, LLVMContext &Context, + SlotMapping *Slots, bool UpgradeDebugInfo, StringRef DataLayoutString) { + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (std::error_code EC = FileOrErr.getError()) { + Err = SMDiagnostic(Filename, SourceMgr::DK_Error, + "Could not open input file: " + EC.message()); + return {nullptr, nullptr}; + } + + return parseAssemblyWithIndex(FileOrErr.get()->getMemBufferRef(), Err, + Context, Slots, UpgradeDebugInfo, + DataLayoutString); +} + std::unique_ptr llvm::parseAssemblyString(StringRef AsmString, SMDiagnostic &Err, LLVMContext &Context, SlotMapping *Slots, @@ -72,13 +108,50 @@ DataLayoutString); } +static bool parseSummaryIndexAssemblyInto(MemoryBufferRef F, + ModuleSummaryIndex &Index, + SMDiagnostic &Err) { + SourceMgr SM; + std::unique_ptr Buf = MemoryBuffer::getMemBuffer(F); + SM.AddNewSourceBuffer(std::move(Buf), SMLoc()); + + // The parser holds a reference to a context that is unused when parsing the + // index, but we need to initialize it. + LLVMContext unusedContext; + return LLParser(F.getBuffer(), SM, Err, nullptr, &Index, unusedContext).Run(); +} + +std::unique_ptr +llvm::parseSummaryIndexAssembly(MemoryBufferRef F, SMDiagnostic &Err) { + std::unique_ptr Index = + make_unique(/*HaveGVs=*/false); + + if (parseSummaryIndexAssemblyInto(F, *Index, Err)) + return nullptr; + + return Index; +} + +std::unique_ptr +llvm::parseSummaryIndexAssemblyFile(StringRef Filename, SMDiagnostic &Err) { + ErrorOr> FileOrErr = + MemoryBuffer::getFileOrSTDIN(Filename); + if (std::error_code EC = FileOrErr.getError()) { + Err = SMDiagnostic(Filename, SourceMgr::DK_Error, + "Could not open input file: " + EC.message()); + return nullptr; + } + + return parseSummaryIndexAssembly(FileOrErr.get()->getMemBufferRef(), Err); +} + Constant *llvm::parseConstantValue(StringRef Asm, SMDiagnostic &Err, const Module &M, const SlotMapping *Slots) { SourceMgr SM; std::unique_ptr Buf = MemoryBuffer::getMemBuffer(Asm); SM.AddNewSourceBuffer(std::move(Buf), SMLoc()); Constant *C; - if (LLParser(Asm, SM, Err, const_cast(&M)) + if (LLParser(Asm, SM, Err, const_cast(&M), nullptr, M.getContext()) .parseStandaloneConstantValue(C, Slots)) return nullptr; return C; @@ -107,7 +180,7 @@ std::unique_ptr Buf = MemoryBuffer::getMemBuffer(Asm); SM.AddNewSourceBuffer(std::move(Buf), SMLoc()); Type *Ty; - if (LLParser(Asm, SM, Err, const_cast(&M)) + if (LLParser(Asm, SM, Err, const_cast(&M), nullptr, M.getContext()) .parseTypeAtBeginning(Ty, Read, Slots)) return nullptr; return Ty; Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -197,7 +197,7 @@ // otherwise we would have a Value for it). If so, synthesize // a value id. for (auto &CallEdge : FS->calls()) - if (!CallEdge.first.getValue()) + if (!CallEdge.first.haveGVs() || !CallEdge.first.getValue()) assignValueId(CallEdge.first.getGUID()); } @@ -230,7 +230,7 @@ // Helper to get the valueId for the type of value recorded in VI. unsigned getValueId(ValueInfo VI) { - if (!VI.getValue()) + if (!VI.haveGVs() || !VI.getValue()) return getValueId(VI.getGUID()); return VE.getValueID(VI.getValue()); } Index: test/Assembler/thinlto-bad-summary1.ll =================================================================== --- test/Assembler/thinlto-bad-summary1.ll +++ test/Assembler/thinlto-bad-summary1.ll @@ -1,8 +1,8 @@ ; Test that we get appropriate error when parsing summary with a missing ; summary type label. -; RUN: not llvm-as %s 2>&1 | FileCheck %s +; RUN: not opt %s 2>&1 | FileCheck %s -; CHECK: error: expected 'label' at start of summary entry +; CHECK: error: Expected 'gv', 'module', or 'typeid' at the start of summary entry ; ModuleID = 'thinlto-function-summary-callgraph.ll' target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" Index: test/Assembler/thinlto-bad-summary2.ll =================================================================== --- test/Assembler/thinlto-bad-summary2.ll +++ test/Assembler/thinlto-bad-summary2.ll @@ -1,6 +1,6 @@ ; 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 +; RUN: not opt %s 2>&1 | FileCheck %s ; CHECK: error: expected '(' at start of summary entry Index: test/Assembler/thinlto-bad-summary3.ll =================================================================== --- test/Assembler/thinlto-bad-summary3.ll +++ test/Assembler/thinlto-bad-summary3.ll @@ -1,6 +1,6 @@ ; Test that we get appropriate error when parsing summary with unbalanced ; parentheses. -; RUN: not llvm-as %s 2>&1 | FileCheck %s +; RUN: not opt %s 2>&1 | FileCheck %s ; CHECK: error: found end of file while parsing summary entry Index: test/Bitcode/thinlto-alias.ll =================================================================== --- test/Bitcode/thinlto-alias.ll +++ test/Bitcode/thinlto-alias.ll @@ -3,9 +3,13 @@ ; 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 +; Round trip it through llvm-as +; RUN: llvm-dis -o - %t2.o | llvm-as -o - | llvm-dis -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 +; Round trip it through llvm-as +; RUN: llvm-dis -o - %t3.thinlto.bc | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=COMBINED-DIS ; CHECK: ; COMBINED: @@ -31,7 +35,7 @@ declare i1 @llvm.type.test(i8*, metadata) nounwind readnone -; DIS: ^0 = module: (path: "{{.*}}thinlto-type-tests.ll.tmp.o", hash: (0, 0, 0, 0, 0)) +; DIS: ^0 = module: (path: "{{.*}}", 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 Index: test/Bitcode/thinlto-type-vcalls.ll =================================================================== --- test/Bitcode/thinlto-type-vcalls.ll +++ test/Bitcode/thinlto-type-vcalls.ll @@ -1,9 +1,13 @@ ; 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 +; Round trip it through llvm-as +; RUN: llvm-dis -o - %t.o | llvm-as -o - | llvm-dis -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 +; Round trip it through llvm-as +; RUN: llvm-dis -o - %t2.thinlto.bc | llvm-as -o - | llvm-dis -o - | FileCheck %s --check-prefix=COMBINED-DIS target datalayout = "e-p:64:64" target triple = "x86_64-unknown-linux-gnu" @@ -106,7 +110,7 @@ declare void @llvm.assume(i1) declare {i8*, i1} @llvm.type.checked.load(i8*, i32, metadata) -; DIS: ^0 = module: (path: "{{.*}}thinlto-type-vcalls.ll.tmp.o", hash: (0, 0, 0, 0, 0)) +; DIS: ^0 = module: (path: "{{.*}}", 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 Index: tools/llvm-as/llvm-as.cpp =================================================================== --- tools/llvm-as/llvm-as.cpp +++ tools/llvm-as/llvm-as.cpp @@ -19,6 +19,7 @@ #include "llvm/Bitcode/BitcodeWriter.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" +#include "llvm/IR/ModuleSummaryIndex.h" #include "llvm/IR/Verifier.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" @@ -63,7 +64,7 @@ cl::value_desc("layout-string"), cl::init("")); -static void WriteOutputFile(const Module *M) { +static void WriteOutputFile(const Module *M, const ModuleSummaryIndex *Index) { // Infer the output filename if needed. if (OutputFilename.empty()) { if (InputFilename == "-") { @@ -83,9 +84,24 @@ exit(1); } - if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) - WriteBitcodeToFile(*M, Out->os(), PreserveBitcodeUseListOrder, nullptr, - EmitModuleHash); + if (Force || !CheckBitcodeOutputToConsole(Out->os(), true)) { + const ModuleSummaryIndex *IndexToWrite = nullptr; + // Don't attempt to write a summary index unless it contains any entries. + // Otherwise we get an empty summary section. + if (Index && Index->begin() != Index->end()) + IndexToWrite = Index; + if (!IndexToWrite || (M && (!M->empty() || !M->global_empty()))) + // If we have a non-empty Module, then we write the Module plus + // any non-null Index along with it as a per-module Index. + // If both are empty, this will give an empty module block, which is + // the expected behavior. + WriteBitcodeToFile(*M, Out->os(), PreserveBitcodeUseListOrder, + IndexToWrite, EmitModuleHash); + else + // Otherwise, with an empty Module but non-empty Index, we write a + // combined index. + WriteIndexToFile(*IndexToWrite, Out->os()); + } // Declare success. Out->keep(); @@ -98,12 +114,14 @@ // Parse the file now... SMDiagnostic Err; - std::unique_ptr M = parseAssemblyFile( + auto ModuleAndIndex = parseAssemblyFileWithIndex( InputFilename, Err, Context, nullptr, !DisableVerify, ClDataLayout); + std::unique_ptr M = std::move(ModuleAndIndex.Mod); if (!M.get()) { Err.print(argv[0], errs()); return 1; } + std::unique_ptr Index = std::move(ModuleAndIndex.Index); if (!DisableVerify) { std::string ErrorStr; @@ -114,13 +132,17 @@ errs() << OS.str(); return 1; } + // TODO: Implement and call summary index verifier. } - if (DumpAsm) + if (DumpAsm) { errs() << "Here's the assembly:\n" << *M.get(); + if (Index.get() && Index->begin() != Index->end()) + Index->print(errs()); + } if (!DisableOutput) - WriteOutputFile(M.get()); + WriteOutputFile(M.get(), Index.get()); return 0; }