[llvm] r364960 - [ThinLTO] Add summary entries for index-based WPD
Mikael Holmén via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 3 00:39:12 PDT 2019
On 2019-07-03 08:39, Mikael Holmén via llvm-commits wrote:
> Hi Teresa,
>
> With this commit I start seeing warnings from gcc 7.4.0:
>
> In file included from ../include/llvm/ADT/STLExtras.h:19:0,
> from ../include/llvm/ADT/StringRef.h:12,
> from ../include/llvm/ADT/StringMap.h:16,
> from ../include/llvm/Support/Host.h:16,
> from ../include/llvm/ADT/Hashing.h:48,
> from ../include/llvm/ADT/ArrayRef.h:12,
> from ../include/llvm/ADT/APFloat.h:20,
> from ../lib/IR/AsmWriter.cpp:18:
> ../include/llvm/ADT/Optional.h: In instantiation of 'void
> llvm::optional_detail::OptionalStorage<T, <anonymous> >::emplace(Args&&
> ...) [with Args = {const std::vector<llvm::TypeIdOffsetVtableInfo,
> std::allocator<llvm::TypeIdOffsetVtableInfo> >}; T = const
> std::vector<llvm::TypeIdOffsetVtableInfo>; bool <anonymous> = false]':
> ../include/llvm/ADT/Optional.h:55:14: required from
> 'llvm::optional_detail::OptionalStorage<T, <anonymous>
> >::OptionalStorage(llvm::optional_detail::OptionalStorage<T,
> <anonymous> >&&) [with T = const
> std::vector<llvm::TypeIdOffsetVtableInfo>; bool <anonymous> = false]'
> ../include/llvm/ADT/Optional.h:228:3: required from here
> ../include/llvm/ADT/Optional.h:89:12: error: cast from type 'const
> std::vector<llvm::TypeIdOffsetVtableInfo>*' to type 'void*' casts away
> qualifiers [-Werror=cast-qual]
> ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
> ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
>
> Can you make any sense out of that with respect to your commit?
>
It seems like it's the const in the return type in
Optional<const TypeIdCompatibleVtableInfo>
getTypeIdCompatibleVtableSummary(StringRef TypeId) const {
auto I = TypeIdCompatibleVtableMap.find(TypeId);
if (I == TypeIdCompatibleVtableMap.end())
return None;
return I->second;
}
that it complains that it casts away in
template <class... Args> void emplace(Args &&... args) {
reset();
::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
hasVal = true;
}
The warning goes away if I remove that const, but I've no idea what the
proper solution is.
/Mikael
> Thanks,
> Mikael
>
> On 2019-07-02 21:38, Teresa Johnson via llvm-commits wrote:
>> Author: tejohnson
>> Date: Tue Jul 2 12:38:02 2019
>> New Revision: 364960
>>
>> URL: http://llvm.org/viewvc/llvm-project?rev=364960&view=rev
>> Log:
>> [ThinLTO] Add summary entries for index-based WPD
>>
>> Summary:
>> If LTOUnit splitting is disabled, the module summary analysis computes
>> the summary information necessary to perform single implementation
>> devirtualization during the thin link with the index and no IR. The
>> information collected from the regular LTO IR in the current hybrid WPD
>> algorithm is summarized, including:
>> 1) For vtable definitions, record the function pointers and their offset
>> within the vtable initializer (subsumes the information collected from
>> IR by tryFindVirtualCallTargets).
>> 2) A record for each type metadata summarizing the vtable definitions
>> decorated with that metadata (subsumes the TypeIdentiferMap collected
>> from IR).
>>
>> Also added are the necessary bitcode records, and the corresponding
>> assembly support.
>>
>> The follow-on index-based WPD patch is D55153.
>>
>> Depends on D53890.
>>
>> Reviewers: pcc
>>
>> Subscribers: mehdi_amini, Prazek, inglorion, eraman, steven_wu, dexonsmith, arphaman, llvm-commits
>>
>> Differential Revision: https://reviews.llvm.org/D54815
>>
>> Added:
>> llvm/trunk/test/Assembler/thinlto-vtable-summary.ll
>> llvm/trunk/test/ThinLTO/X86/devirt.ll
>> Modified:
>> llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h
>> llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h
>> llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp
>> llvm/trunk/lib/AsmParser/LLLexer.cpp
>> llvm/trunk/lib/AsmParser/LLParser.cpp
>> llvm/trunk/lib/AsmParser/LLParser.h
>> llvm/trunk/lib/AsmParser/LLToken.h
>> llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp
>> llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp
>> llvm/trunk/lib/IR/AsmWriter.cpp
>> llvm/trunk/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
>> llvm/trunk/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
>>
>> Modified: llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h (original)
>> +++ llvm/trunk/include/llvm/Bitcode/LLVMBitCodes.h Tue Jul 2 12:38:02 2019
>> @@ -263,10 +263,31 @@ enum GlobalValueSummarySymtabCodes {
>> // Index-wide flags
>> FS_FLAGS = 20,
>> // Maps type identifier to summary information for that type identifier.
>> + // Produced by the thin link (only lives in combined index).
>> // TYPE_ID: [typeid, kind, bitwidth, align, size, bitmask, inlinebits,
>> // n x (typeid, kind, name, numrba,
>> // numrba x (numarg, numarg x arg, kind, info, byte, bit))]
>> FS_TYPE_ID = 21,
>> + // For background see overview at https://llvm.org/docs/TypeMetadata.html.
>> + // The type metadata includes both the type identifier and the offset of
>> + // the address point of the type (the address held by objects of that type
>> + // which may not be the beginning of the virtual table). Vtable definitions
>> + // are decorated with type metadata for the types they are compatible with.
>> + //
>> + // Maps type identifier to summary information for that type identifier
>> + // computed from type metadata: the valueid of each vtable definition
>> + // decorated with a type metadata for that identifier, and the offset from
>> + // the corresponding type metadata.
>> + // Exists in the per-module summary to provide information to thin link
>> + // for index-based whole program devirtualization.
>> + // TYPE_ID_METADATA: [typeid, n x (valueid, offset)]
>> + FS_TYPE_ID_METADATA = 22,
>> + // Summarizes vtable definition for use in index-based whole program
>> + // devirtualization during the thin link.
>> + // PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: [valueid, flags, varflags,
>> + // numrefs, numrefs x valueid,
>> + // n x (valueid, offset)]
>> + FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS = 23,
>> };
>>
>> enum MetadataCodes {
>>
>> Modified: llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h (original)
>> +++ llvm/trunk/include/llvm/IR/ModuleSummaryIndex.h Tue Jul 2 12:38:02 2019
>> @@ -698,6 +698,18 @@ template <> struct DenseMapInfo<Function
>> }
>> };
>>
>> +/// The ValueInfo and offset for a function within a vtable definition
>> +/// initializer array.
>> +struct VirtFuncOffset {
>> + VirtFuncOffset(ValueInfo VI, uint64_t Offset)
>> + : FuncVI(VI), VTableOffset(Offset) {}
>> +
>> + ValueInfo FuncVI;
>> + uint64_t VTableOffset;
>> +};
>> +/// List of functions referenced by a particular vtable definition.
>> +using VTableFuncList = std::vector<VirtFuncOffset>;
>> +
>> /// Global variable summary information to aid decisions and
>> /// implementation of importing.
>> ///
>> @@ -705,6 +717,11 @@ template <> struct DenseMapInfo<Function
>> /// modified during the program run or not. This affects ThinLTO
>> /// internalization
>> class GlobalVarSummary : public GlobalValueSummary {
>> +private:
>> + /// For vtable definitions this holds the list of functions and
>> + /// their corresponding offsets within the initializer array.
>> + std::unique_ptr<VTableFuncList> VTableFuncs;
>> +
>> public:
>> struct GVarFlags {
>> GVarFlags(bool ReadOnly = false) : ReadOnly(ReadOnly) {}
>> @@ -725,6 +742,17 @@ public:
>> GVarFlags varflags() const { return VarFlags; }
>> void setReadOnly(bool RO) { VarFlags.ReadOnly = RO; }
>> bool isReadOnly() const { return VarFlags.ReadOnly; }
>> +
>> + void setVTableFuncs(VTableFuncList Funcs) {
>> + assert(!VTableFuncs);
>> + VTableFuncs = llvm::make_unique<VTableFuncList>(std::move(Funcs));
>> + }
>> +
>> + ArrayRef<VirtFuncOffset> vTableFuncs() const {
>> + if (VTableFuncs)
>> + return *VTableFuncs;
>> + return {};
>> + }
>> };
>>
>> struct TypeTestResolution {
>> @@ -823,6 +851,29 @@ using GVSummaryMapTy = DenseMap<GlobalVa
>> using TypeIdSummaryMapTy =
>> std::multimap<GlobalValue::GUID, std::pair<std::string, TypeIdSummary>>;
>>
>> +/// The following data structures summarize type metadata information.
>> +/// For type metadata overview see https://llvm.org/docs/TypeMetadata.html.
>> +/// Each type metadata includes both the type identifier and the offset of
>> +/// the address point of the type (the address held by objects of that type
>> +/// which may not be the beginning of the virtual table). Vtable definitions
>> +/// are decorated with type metadata for the types they are compatible with.
>> +///
>> +/// Holds information about vtable definitions decorated with type metadata:
>> +/// the vtable definition value and its address point offset in a type
>> +/// identifier metadata it is decorated (compatible) with.
>> +struct TypeIdOffsetVtableInfo {
>> + TypeIdOffsetVtableInfo(uint64_t Offset, ValueInfo VI)
>> + : AddressPointOffset(Offset), VTableVI(VI) {}
>> +
>> + uint64_t AddressPointOffset;
>> + ValueInfo VTableVI;
>> +};
>> +/// List of vtable definitions decorated by a particular type identifier,
>> +/// and their corresponding offsets in that type identifier's metadata.
>> +/// Note that each type identifier may be compatible with multiple vtables, due
>> +/// to inheritance, which is why this is a vector.
>> +using TypeIdCompatibleVtableInfo = std::vector<TypeIdOffsetVtableInfo>;
>> +
>> /// Class to hold module path string table and global value map,
>> /// and encapsulate methods for operating on them.
>> class ModuleSummaryIndex {
>> @@ -835,9 +886,15 @@ private:
>> ModulePathStringTableTy ModulePathStringTable;
>>
>> /// Mapping from type identifier GUIDs to type identifier and its summary
>> - /// information.
>> + /// information. Produced by thin link.
>> TypeIdSummaryMapTy TypeIdMap;
>>
>> + /// Mapping from type identifier to information about vtables decorated
>> + /// with that type identifier's metadata. Produced by per module summary
>> + /// analysis and consumed by thin link. For more information, see description
>> + /// above where TypeIdCompatibleVtableInfo is defined.
>> + std::map<std::string, TypeIdCompatibleVtableInfo> TypeIdCompatibleVtableMap;
>> +
>> /// Mapping from original ID to GUID. If original ID can map to multiple
>> /// GUIDs, it will be mapped to 0.
>> std::map<GlobalValue::GUID, GlobalValue::GUID> OidGuidMap;
>> @@ -1201,6 +1258,29 @@ public:
>> return nullptr;
>> }
>>
>> + const std::map<std::string, TypeIdCompatibleVtableInfo> &
>> + typeIdCompatibleVtableMap() const {
>> + return TypeIdCompatibleVtableMap;
>> + }
>> +
>> + /// Return an existing or new TypeIdCompatibleVtableMap entry for \p TypeId.
>> + /// This accessor can mutate the map and therefore should not be used in
>> + /// the ThinLTO backends.
>> + TypeIdCompatibleVtableInfo &
>> + getOrInsertTypeIdCompatibleVtableSummary(StringRef TypeId) {
>> + return TypeIdCompatibleVtableMap[TypeId];
>> + }
>> +
>> + /// For the given \p TypeId, this returns the TypeIdCompatibleVtableMap
>> + /// entry if present in the summary map. This may be used when importing.
>> + Optional<const TypeIdCompatibleVtableInfo>
>> + getTypeIdCompatibleVtableSummary(StringRef TypeId) const {
>> + auto I = TypeIdCompatibleVtableMap.find(TypeId);
>> + if (I == TypeIdCompatibleVtableMap.end())
>> + return None;
>> + return I->second;
>> + }
>> +
>> /// Collect for the given module the list of functions it defines
>> /// (GUID -> Summary).
>> void collectDefinedFunctionsForModule(StringRef ModulePath,
>>
>> Modified: llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp (original)
>> +++ llvm/trunk/lib/Analysis/ModuleSummaryAnalysis.cpp Tue Jul 2 12:38:02 2019
>> @@ -411,9 +411,98 @@ static void computeFunctionSummary(Modul
>> Index.addGlobalValueSummary(F, std::move(FuncSummary));
>> }
>>
>> +/// Find function pointers referenced within the given vtable initializer
>> +/// (or subset of an initializer) \p I. The starting offset of \p I within
>> +/// the vtable initializer is \p StartingOffset. Any discovered function
>> +/// pointers are added to \p VTableFuncs along with their cumulative offset
>> +/// within the initializer.
>> +static void findFuncPointers(const Constant *I, uint64_t StartingOffset,
>> + const Module &M, ModuleSummaryIndex &Index,
>> + VTableFuncList &VTableFuncs) {
>> + // First check if this is a function pointer.
>> + if (I->getType()->isPointerTy()) {
>> + auto Fn = dyn_cast<Function>(I->stripPointerCasts());
>> + // We can disregard __cxa_pure_virtual as a possible call target, as
>> + // calls to pure virtuals are UB.
>> + if (Fn && Fn->getName() != "__cxa_pure_virtual")
>> + VTableFuncs.push_back({Index.getOrInsertValueInfo(Fn), StartingOffset});
>> + return;
>> + }
>> +
>> + // Walk through the elements in the constant struct or array and recursively
>> + // look for virtual function pointers.
>> + const DataLayout &DL = M.getDataLayout();
>> + if (auto *C = dyn_cast<ConstantStruct>(I)) {
>> + StructType *STy = dyn_cast<StructType>(C->getType());
>> + assert(STy);
>> + const StructLayout *SL = DL.getStructLayout(C->getType());
>> +
>> + for (StructType::element_iterator EB = STy->element_begin(), EI = EB,
>> + EE = STy->element_end();
>> + EI != EE; ++EI) {
>> + auto Offset = SL->getElementOffset(EI - EB);
>> + unsigned Op = SL->getElementContainingOffset(Offset);
>> + findFuncPointers(cast<Constant>(I->getOperand(Op)),
>> + StartingOffset + Offset, M, Index, VTableFuncs);
>> + }
>> + } else if (auto *C = dyn_cast<ConstantArray>(I)) {
>> + ArrayType *ATy = C->getType();
>> + Type *EltTy = ATy->getElementType();
>> + uint64_t EltSize = DL.getTypeAllocSize(EltTy);
>> + for (unsigned i = 0, e = ATy->getNumElements(); i != e; ++i) {
>> + findFuncPointers(cast<Constant>(I->getOperand(i)),
>> + StartingOffset + i * EltSize, M, Index, VTableFuncs);
>> + }
>> + }
>> +}
>> +
>> +// Identify the function pointers referenced by vtable definition \p V.
>> +static void computeVTableFuncs(ModuleSummaryIndex &Index,
>> + const GlobalVariable &V, const Module &M,
>> + VTableFuncList &VTableFuncs) {
>> + if (!V.isConstant())
>> + return;
>> +
>> + findFuncPointers(V.getInitializer(), /*StartingOffset=*/0, M, Index,
>> + VTableFuncs);
>> +
>> +#ifndef NDEBUG
>> + // Validate that the VTableFuncs list is ordered by offset.
>> + uint64_t PrevOffset = 0;
>> + for (auto &P : VTableFuncs) {
>> + // The findVFuncPointers traversal should have encountered the
>> + // functions in offset order. We need to use ">=" since PrevOffset
>> + // starts at 0.
>> + assert(P.VTableOffset >= PrevOffset);
>> + PrevOffset = P.VTableOffset;
>> + }
>> +#endif
>> +}
>> +
>> +/// Record vtable definition \p V for each type metadata it references.
>> static void
>> -computeVariableSummary(ModuleSummaryIndex &Index, const GlobalVariable &V,
>> - DenseSet<GlobalValue::GUID> &CantBePromoted) {
>> +recordTypeIdCompatibleVtableReferences(ModuleSummaryIndex &Index,
>> + const GlobalVariable &V,
>> + SmallVectorImpl<MDNode *> &Types) {
>> + for (MDNode *Type : Types) {
>> + auto TypeID = Type->getOperand(1).get();
>> +
>> + uint64_t Offset =
>> + cast<ConstantInt>(
>> + cast<ConstantAsMetadata>(Type->getOperand(0))->getValue())
>> + ->getZExtValue();
>> +
>> + if (auto *TypeId = dyn_cast<MDString>(TypeID))
>> + Index.getOrInsertTypeIdCompatibleVtableSummary(TypeId->getString())
>> + .push_back({Offset, Index.getOrInsertValueInfo(&V)});
>> + }
>> +}
>> +
>> +static void computeVariableSummary(ModuleSummaryIndex &Index,
>> + const GlobalVariable &V,
>> + DenseSet<GlobalValue::GUID> &CantBePromoted,
>> + const Module &M,
>> + SmallVectorImpl<MDNode *> &Types) {
>> SetVector<ValueInfo> RefEdges;
>> SmallPtrSet<const User *, 8> Visited;
>> bool HasBlockAddress = findRefEdges(Index, &V, RefEdges, Visited);
>> @@ -422,6 +511,21 @@ computeVariableSummary(ModuleSummaryInde
>> /* Live = */ false, V.isDSOLocal(),
>> V.hasLinkOnceODRLinkage() && V.hasGlobalUnnamedAddr());
>>
>> + VTableFuncList VTableFuncs;
>> + // If splitting is not enabled, then we compute the summary information
>> + // necessary for index-based whole program devirtualization.
>> + if (!Index.enableSplitLTOUnit()) {
>> + Types.clear();
>> + V.getMetadata(LLVMContext::MD_type, Types);
>> + if (!Types.empty()) {
>> + // Identify the function pointers referenced by this vtable definition.
>> + computeVTableFuncs(Index, V, M, VTableFuncs);
>> +
>> + // Record this vtable definition for each type metadata it references.
>> + recordTypeIdCompatibleVtableReferences(Index, V, Types);
>> + }
>> + }
>> +
>> // Don't mark variables we won't be able to internalize as read-only.
>> GlobalVarSummary::GVarFlags VarFlags(
>> !V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() &&
>> @@ -432,6 +536,8 @@ computeVariableSummary(ModuleSummaryInde
>> CantBePromoted.insert(V.getGUID());
>> if (HasBlockAddress)
>> GVarSummary->setNotEligibleToImport();
>> + if (!VTableFuncs.empty())
>> + GVarSummary->setVTableFuncs(VTableFuncs);
>> Index.addGlobalValueSummary(V, std::move(GVarSummary));
>> }
>>
>> @@ -578,10 +684,11 @@ ModuleSummaryIndex llvm::buildModuleSumm
>>
>> // Compute summaries for all variables defined in module, and save in the
>> // index.
>> + SmallVector<MDNode *, 2> Types;
>> for (const GlobalVariable &G : M.globals()) {
>> if (G.isDeclaration())
>> continue;
>> - computeVariableSummary(Index, G, CantBePromoted);
>> + computeVariableSummary(Index, G, CantBePromoted, M, Types);
>> }
>>
>> // Compute summaries for all aliases defined in module, and save in the
>>
>> Modified: llvm/trunk/lib/AsmParser/LLLexer.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/AsmParser/LLLexer.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/AsmParser/LLLexer.cpp (original)
>> +++ llvm/trunk/lib/AsmParser/LLLexer.cpp Tue Jul 2 12:38:02 2019
>> @@ -752,6 +752,8 @@ lltok::Kind LLLexer::LexIdentifier() {
>> KEYWORD(critical);
>> KEYWORD(relbf);
>> KEYWORD(variable);
>> + KEYWORD(vTableFuncs);
>> + KEYWORD(virtFunc);
>> KEYWORD(aliasee);
>> KEYWORD(refs);
>> KEYWORD(typeIdInfo);
>> @@ -764,6 +766,7 @@ lltok::Kind LLLexer::LexIdentifier() {
>> KEYWORD(offset);
>> KEYWORD(args);
>> KEYWORD(typeid);
>> + KEYWORD(typeidCompatibleVTable);
>> KEYWORD(summary);
>> KEYWORD(typeTestRes);
>> KEYWORD(kind);
>>
>> Modified: llvm/trunk/lib/AsmParser/LLParser.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/AsmParser/LLParser.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/AsmParser/LLParser.cpp (original)
>> +++ llvm/trunk/lib/AsmParser/LLParser.cpp Tue Jul 2 12:38:02 2019
>> @@ -832,6 +832,9 @@ bool LLParser::ParseSummaryEntry() {
>> case lltok::kw_typeid:
>> result = ParseTypeIdEntry(SummaryID);
>> break;
>> + case lltok::kw_typeidCompatibleVTable:
>> + result = ParseTypeIdCompatibleVtableEntry(SummaryID);
>> + break;
>> default:
>> result = Error(Lex.getLoc(), "unexpected summary kind");
>> break;
>> @@ -7486,6 +7489,92 @@ bool LLParser::ParseTypeIdSummary(TypeId
>> return false;
>> }
>>
>> +static ValueInfo EmptyVI =
>> + ValueInfo(false, (GlobalValueSummaryMapTy::value_type *)-8);
>> +
>> +/// TypeIdCompatibleVtableEntry
>> +/// ::= 'typeidCompatibleVTable' ':' '(' 'name' ':' STRINGCONSTANT ','
>> +/// TypeIdCompatibleVtableInfo
>> +/// ')'
>> +bool LLParser::ParseTypeIdCompatibleVtableEntry(unsigned ID) {
>> + assert(Lex.getKind() == lltok::kw_typeidCompatibleVTable);
>> + 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;
>> +
>> + TypeIdCompatibleVtableInfo &TI =
>> + Index->getOrInsertTypeIdCompatibleVtableSummary(Name);
>> + if (ParseToken(lltok::comma, "expected ',' here") ||
>> + ParseToken(lltok::kw_summary, "expected 'summary' here") ||
>> + ParseToken(lltok::colon, "expected ':' here") ||
>> + ParseToken(lltok::lparen, "expected '(' here"))
>> + return true;
>> +
>> + IdToIndexMapType IdToIndexMap;
>> + // Parse each call edge
>> + do {
>> + uint64_t Offset;
>> + 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"))
>> + return true;
>> +
>> + LocTy Loc = Lex.getLoc();
>> + unsigned GVId;
>> + ValueInfo VI;
>> + if (ParseGVReference(VI, GVId))
>> + return true;
>> +
>> + // Keep track of the TypeIdCompatibleVtableInfo 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(TI.size(), Loc));
>> + TI.push_back({Offset, VI});
>> +
>> + if (ParseToken(lltok::rparen, "expected ')' in call"))
>> + return true;
>> + } while (EatIfPresent(lltok::comma));
>> +
>> + // Now that the TI 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(TI[P.first].VTableVI == EmptyVI &&
>> + "Forward referenced ValueInfo expected to be empty");
>> + auto FwdRef = ForwardRefValueInfos.insert(std::make_pair(
>> + I.first, std::vector<std::pair<ValueInfo *, LocTy>>()));
>> + FwdRef.first->second.push_back(
>> + std::make_pair(&TI[P.first].VTableVI, P.second));
>> + }
>> + }
>> +
>> + if (ParseToken(lltok::rparen, "expected ')' here") ||
>> + 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;
>> +}
>> +
>> /// TypeTestResolution
>> /// ::= 'typeTestRes' ':' '(' 'kind' ':'
>> /// ( 'unsat' | 'byteArray' | 'inline' | 'single' | 'allOnes' ) ','
>> @@ -7994,6 +8083,7 @@ bool LLParser::ParseVariableSummary(std:
>> /*Live=*/false, /*IsLocal=*/false, /*CanAutoHide=*/false);
>> GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false);
>> std::vector<ValueInfo> Refs;
>> + VTableFuncList VTableFuncs;
>> if (ParseToken(lltok::colon, "expected ':' here") ||
>> ParseToken(lltok::lparen, "expected '(' here") ||
>> ParseModuleReference(ModulePath) ||
>> @@ -8002,10 +8092,20 @@ bool LLParser::ParseVariableSummary(std:
>> ParseGVarFlags(GVarFlags))
>> return true;
>>
>> - // Parse optional refs field
>> - if (EatIfPresent(lltok::comma)) {
>> - if (ParseOptionalRefs(Refs))
>> - return true;
>> + // Parse optional fields
>> + while (EatIfPresent(lltok::comma)) {
>> + switch (Lex.getKind()) {
>> + case lltok::kw_vTableFuncs:
>> + if (ParseOptionalVTableFuncs(VTableFuncs))
>> + return true;
>> + break;
>> + case lltok::kw_refs:
>> + if (ParseOptionalRefs(Refs))
>> + return true;
>> + break;
>> + default:
>> + return Error(Lex.getLoc(), "expected optional variable summary field");
>> + }
>> }
>>
>> if (ParseToken(lltok::rparen, "expected ')' here"))
>> @@ -8015,6 +8115,7 @@ bool LLParser::ParseVariableSummary(std:
>> llvm::make_unique<GlobalVarSummary>(GVFlags, GVarFlags, std::move(Refs));
>>
>> GS->setModulePath(ModulePath);
>> + GS->setVTableFuncs(std::move(VTableFuncs));
>>
>> AddGlobalValueToIndex(Name, GUID, (GlobalValue::LinkageTypes)GVFlags.Linkage,
>> ID, std::move(GS));
>> @@ -8235,6 +8336,67 @@ bool LLParser::ParseHotness(CalleeInfo::
>> return false;
>> }
>>
>> +/// OptionalVTableFuncs
>> +/// := 'vTableFuncs' ':' '(' VTableFunc [',' VTableFunc]* ')'
>> +/// VTableFunc ::= '(' 'virtFunc' ':' GVReference ',' 'offset' ':' UInt64 ')'
>> +bool LLParser::ParseOptionalVTableFuncs(VTableFuncList &VTableFuncs) {
>> + assert(Lex.getKind() == lltok::kw_vTableFuncs);
>> + Lex.Lex();
>> +
>> + if (ParseToken(lltok::colon, "expected ':' in vTableFuncs") |
>> + ParseToken(lltok::lparen, "expected '(' in vTableFuncs"))
>> + return true;
>> +
>> + IdToIndexMapType IdToIndexMap;
>> + // Parse each virtual function pair
>> + do {
>> + ValueInfo VI;
>> + if (ParseToken(lltok::lparen, "expected '(' in vTableFunc") ||
>> + ParseToken(lltok::kw_virtFunc, "expected 'callee' in vTableFunc") ||
>> + ParseToken(lltok::colon, "expected ':'"))
>> + return true;
>> +
>> + LocTy Loc = Lex.getLoc();
>> + unsigned GVId;
>> + if (ParseGVReference(VI, GVId))
>> + return true;
>> +
>> + uint64_t Offset;
>> + if (ParseToken(lltok::comma, "expected comma") ||
>> + ParseToken(lltok::kw_offset, "expected offset") ||
>> + ParseToken(lltok::colon, "expected ':'") || ParseUInt64(Offset))
>> + return true;
>> +
>> + // Keep track of the VTableFuncs 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(VTableFuncs.size(), Loc));
>> + VTableFuncs.push_back({VI, Offset});
>> +
>> + if (ParseToken(lltok::rparen, "expected ')' in vTableFunc"))
>> + return true;
>> + } while (EatIfPresent(lltok::comma));
>> +
>> + // Now that the VTableFuncs 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(VTableFuncs[P.first].FuncVI == EmptyVI &&
>> + "Forward referenced ValueInfo expected to be empty");
>> + auto FwdRef = ForwardRefValueInfos.insert(std::make_pair(
>> + I.first, std::vector<std::pair<ValueInfo *, LocTy>>()));
>> + FwdRef.first->second.push_back(
>> + std::make_pair(&VTableFuncs[P.first].FuncVI, P.second));
>> + }
>> + }
>> +
>> + if (ParseToken(lltok::rparen, "expected ')' in vTableFuncs"))
>> + return true;
>> +
>> + return false;
>> +}
>> +
>> /// OptionalRefs
>> /// := 'refs' ':' '(' GVReference [',' GVReference]* ')'
>> bool LLParser::ParseOptionalRefs(std::vector<ValueInfo> &Refs) {
>>
>> Modified: llvm/trunk/lib/AsmParser/LLParser.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/AsmParser/LLParser.h?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/AsmParser/LLParser.h (original)
>> +++ llvm/trunk/lib/AsmParser/LLParser.h Tue Jul 2 12:38:02 2019
>> @@ -369,9 +369,11 @@ namespace llvm {
>> IdToIndexMapType &IdToIndexMap, unsigned Index);
>> bool ParseVFuncId(FunctionSummary::VFuncId &VFuncId,
>> IdToIndexMapType &IdToIndexMap, unsigned Index);
>> + bool ParseOptionalVTableFuncs(VTableFuncList &VTableFuncs);
>> bool ParseOptionalRefs(std::vector<ValueInfo> &Refs);
>> bool ParseTypeIdEntry(unsigned ID);
>> bool ParseTypeIdSummary(TypeIdSummary &TIS);
>> + bool ParseTypeIdCompatibleVtableEntry(unsigned ID);
>> bool ParseTypeTestResolution(TypeTestResolution &TTRes);
>> bool ParseOptionalWpdResolutions(
>> std::map<uint64_t, WholeProgramDevirtResolution> &WPDResMap);
>>
>> Modified: llvm/trunk/lib/AsmParser/LLToken.h
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/AsmParser/LLToken.h?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/AsmParser/LLToken.h (original)
>> +++ llvm/trunk/lib/AsmParser/LLToken.h Tue Jul 2 12:38:02 2019
>> @@ -383,6 +383,8 @@ enum Kind {
>> kw_critical,
>> kw_relbf,
>> kw_variable,
>> + kw_vTableFuncs,
>> + kw_virtFunc,
>> kw_aliasee,
>> kw_refs,
>> kw_typeIdInfo,
>> @@ -395,6 +397,7 @@ enum Kind {
>> kw_offset,
>> kw_args,
>> kw_typeid,
>> + kw_typeidCompatibleVTable,
>> kw_summary,
>> kw_typeTestRes,
>> kw_kind,
>>
>> Modified: llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp (original)
>> +++ llvm/trunk/lib/Bitcode/Reader/BitcodeReader.cpp Tue Jul 2 12:38:02 2019
>> @@ -828,6 +828,9 @@ private:
>> bool HasRelBF);
>> Error parseEntireSummary(unsigned ID);
>> Error parseModuleStringTable();
>> + void parseTypeIdCompatibleVtableSummaryRecord(ArrayRef<uint64_t> Record);
>> + void parseTypeIdCompatibleVtableInfo(ArrayRef<uint64_t> Record, size_t &Slot,
>> + TypeIdCompatibleVtableInfo &TypeId);
>>
>> std::pair<ValueInfo, GlobalValue::GUID>
>> getValueInfoFromValueId(unsigned ValueId);
>> @@ -5657,6 +5660,27 @@ static void parseTypeIdSummaryRecord(Arr
>> parseWholeProgramDevirtResolution(Record, Strtab, Slot, TypeId);
>> }
>>
>> +void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableInfo(
>> + ArrayRef<uint64_t> Record, size_t &Slot,
>> + TypeIdCompatibleVtableInfo &TypeId) {
>> + uint64_t Offset = Record[Slot++];
>> + ValueInfo Callee = getValueInfoFromValueId(Record[Slot++]).first;
>> + TypeId.push_back({Offset, Callee});
>> +}
>> +
>> +void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableSummaryRecord(
>> + ArrayRef<uint64_t> Record) {
>> + size_t Slot = 0;
>> + TypeIdCompatibleVtableInfo &TypeId =
>> + TheIndex.getOrInsertTypeIdCompatibleVtableSummary(
>> + {Strtab.data() + Record[Slot],
>> + static_cast<size_t>(Record[Slot + 1])});
>> + Slot += 2;
>> +
>> + while (Slot < Record.size())
>> + parseTypeIdCompatibleVtableInfo(Record, Slot, TypeId);
>> +}
>> +
>> static void setImmutableRefs(std::vector<ValueInfo> &Refs, unsigned Count) {
>> // Read-only refs are in the end of the refs list.
>> for (unsigned RefNo = Refs.size() - Count; RefNo < Refs.size(); ++RefNo)
>> @@ -5883,6 +5907,34 @@ Error ModuleSummaryIndexBitcodeReader::p
>> TheIndex.addGlobalValueSummary(GUID.first, std::move(FS));
>> break;
>> }
>> + // FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: [valueid, flags, varflags,
>> + // numrefs, numrefs x valueid,
>> + // n x (valueid, offset)]
>> + case bitc::FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS: {
>> + unsigned ValueID = Record[0];
>> + uint64_t RawFlags = Record[1];
>> + GlobalVarSummary::GVarFlags GVF = getDecodedGVarFlags(Record[2]);
>> + unsigned NumRefs = Record[3];
>> + unsigned RefListStartIndex = 4;
>> + unsigned VTableListStartIndex = RefListStartIndex + NumRefs;
>> + auto Flags = getDecodedGVSummaryFlags(RawFlags, Version);
>> + std::vector<ValueInfo> Refs = makeRefList(
>> + ArrayRef<uint64_t>(Record).slice(RefListStartIndex, NumRefs));
>> + VTableFuncList VTableFuncs;
>> + for (unsigned I = VTableListStartIndex, E = Record.size(); I != E; ++I) {
>> + ValueInfo Callee = getValueInfoFromValueId(Record[I]).first;
>> + uint64_t Offset = Record[++I];
>> + VTableFuncs.push_back({Callee, Offset});
>> + }
>> + auto VS =
>> + llvm::make_unique<GlobalVarSummary>(Flags, GVF, std::move(Refs));
>> + VS->setModulePath(getThisModule()->first());
>> + VS->setVTableFuncs(VTableFuncs);
>> + auto GUID = getValueInfoFromValueId(ValueID);
>> + VS->setOriginalName(GUID.second);
>> + TheIndex.addGlobalValueSummary(GUID.first, std::move(VS));
>> + break;
>> + }
>> // FS_COMBINED: [valueid, modid, flags, instcount, fflags, numrefs,
>> // numrefs x valueid, n x (valueid)]
>> // FS_COMBINED_PROFILE: [valueid, modid, flags, instcount, fflags, numrefs,
>> @@ -6049,6 +6101,10 @@ Error ModuleSummaryIndexBitcodeReader::p
>> case bitc::FS_TYPE_ID:
>> parseTypeIdSummaryRecord(Record, Strtab, TheIndex);
>> break;
>> +
>> + case bitc::FS_TYPE_ID_METADATA:
>> + parseTypeIdCompatibleVtableSummaryRecord(Record);
>> + break;
>> }
>> }
>> llvm_unreachable("Exit infinite loop");
>>
>> Modified: llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp (original)
>> +++ llvm/trunk/lib/Bitcode/Writer/BitcodeWriter.cpp Tue Jul 2 12:38:02 2019
>> @@ -214,7 +214,8 @@ private:
>> const Function &F);
>> void writeModuleLevelReferences(const GlobalVariable &V,
>> SmallVector<uint64_t, 64> &NameVals,
>> - unsigned FSModRefsAbbrev);
>> + unsigned FSModRefsAbbrev,
>> + unsigned FSModVTableRefsAbbrev);
>>
>> void assignValueId(GlobalValue::GUID ValGUID) {
>> GUIDToValueIdMap[ValGUID] = ++GlobalValueId;
>> @@ -3605,6 +3606,19 @@ static void writeTypeIdSummaryRecord(Sma
>> W.second);
>> }
>>
>> +static void writeTypeIdCompatibleVtableSummaryRecord(
>> + SmallVector<uint64_t, 64> &NameVals, StringTableBuilder &StrtabBuilder,
>> + const std::string &Id, const TypeIdCompatibleVtableInfo &Summary,
>> + ValueEnumerator &VE) {
>> + NameVals.push_back(StrtabBuilder.add(Id));
>> + NameVals.push_back(Id.size());
>> +
>> + for (auto &P : Summary) {
>> + NameVals.push_back(P.AddressPointOffset);
>> + NameVals.push_back(VE.getValueID(P.VTableVI.getValue()));
>> + }
>> +}
>> +
>> // Helper to emit a single function summary record.
>> void ModuleBitcodeWriterBase::writePerModuleFunctionSummaryRecord(
>> SmallVector<uint64_t, 64> &NameVals, GlobalValueSummary *Summary,
>> @@ -3649,7 +3663,7 @@ void ModuleBitcodeWriterBase::writePerMo
>> // and emit them in a summary record.
>> void ModuleBitcodeWriterBase::writeModuleLevelReferences(
>> const GlobalVariable &V, SmallVector<uint64_t, 64> &NameVals,
>> - unsigned FSModRefsAbbrev) {
>> + unsigned FSModRefsAbbrev, unsigned FSModVTableRefsAbbrev) {
>> auto VI = Index->getValueInfo(V.getGUID());
>> if (!VI || VI.getSummaryList().empty()) {
>> // Only declarations should not have a summary (a declaration might however
>> @@ -3663,6 +3677,10 @@ void ModuleBitcodeWriterBase::writeModul
>> NameVals.push_back(getEncodedGVSummaryFlags(VS->flags()));
>> NameVals.push_back(getEncodedGVarFlags(VS->varflags()));
>>
>> + auto VTableFuncs = VS->vTableFuncs();
>> + if (!VTableFuncs.empty())
>> + NameVals.push_back(VS->refs().size());
>> +
>> unsigned SizeBeforeRefs = NameVals.size();
>> for (auto &RI : VS->refs())
>> NameVals.push_back(VE.getValueID(RI.getValue()));
>> @@ -3670,8 +3688,20 @@ void ModuleBitcodeWriterBase::writeModul
>> // been initialized from a DenseSet.
>> llvm::sort(NameVals.begin() + SizeBeforeRefs, NameVals.end());
>>
>> - Stream.EmitRecord(bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS, NameVals,
>> - FSModRefsAbbrev);
>> + if (!VTableFuncs.empty()) {
>> + // VTableFuncs pairs should already be sorted by offset.
>> + for (auto &P : VTableFuncs) {
>> + NameVals.push_back(VE.getValueID(P.FuncVI.getValue()));
>> + NameVals.push_back(P.VTableOffset);
>> + }
>> + }
>> +
>> + if (VTableFuncs.empty())
>> + Stream.EmitRecord(bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS, NameVals,
>> + FSModRefsAbbrev);
>> + else
>> + Stream.EmitRecord(bitc::FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS, NameVals,
>> + FSModVTableRefsAbbrev);
>> NameVals.clear();
>> }
>>
>> @@ -3752,6 +3782,17 @@ void ModuleBitcodeWriterBase::writePerMo
>> Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
>> unsigned FSModRefsAbbrev = Stream.EmitAbbrev(std::move(Abbv));
>>
>> + // Abbrev for FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS.
>> + Abbv = std::make_shared<BitCodeAbbrev>();
>> + Abbv->Add(BitCodeAbbrevOp(bitc::FS_PERMODULE_VTABLE_GLOBALVAR_INIT_REFS));
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs
>> + // numrefs x valueid, n x (valueid , offset)
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
>> + unsigned FSModVTableRefsAbbrev = Stream.EmitAbbrev(std::move(Abbv));
>> +
>> // Abbrev for FS_ALIAS.
>> Abbv = std::make_shared<BitCodeAbbrev>();
>> Abbv->Add(BitCodeAbbrevOp(bitc::FS_ALIAS));
>> @@ -3760,6 +3801,16 @@ void ModuleBitcodeWriterBase::writePerMo
>> Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid
>> unsigned FSAliasAbbrev = Stream.EmitAbbrev(std::move(Abbv));
>>
>> + // Abbrev for FS_TYPE_ID_METADATA
>> + Abbv = std::make_shared<BitCodeAbbrev>();
>> + Abbv->Add(BitCodeAbbrevOp(bitc::FS_TYPE_ID_METADATA));
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // typeid strtab index
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // typeid length
>> + // n x (valueid , offset)
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
>> + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8));
>> + unsigned TypeIdCompatibleVtableAbbrev = Stream.EmitAbbrev(std::move(Abbv));
>> +
>> SmallVector<uint64_t, 64> NameVals;
>> // Iterate over the list of functions instead of the Index to
>> // ensure the ordering is stable.
>> @@ -3784,7 +3835,8 @@ void ModuleBitcodeWriterBase::writePerMo
>> // Capture references from GlobalVariable initializers, which are outside
>> // of a function scope.
>> for (const GlobalVariable &G : M.globals())
>> - writeModuleLevelReferences(G, NameVals, FSModRefsAbbrev);
>> + writeModuleLevelReferences(G, NameVals, FSModRefsAbbrev,
>> + FSModVTableRefsAbbrev);
>>
>> for (const GlobalAlias &A : M.aliases()) {
>> auto *Aliasee = A.getBaseObject();
>> @@ -3802,6 +3854,16 @@ void ModuleBitcodeWriterBase::writePerMo
>> NameVals.clear();
>> }
>>
>> + if (!Index->typeIdCompatibleVtableMap().empty()) {
>> + for (auto &S : Index->typeIdCompatibleVtableMap()) {
>> + writeTypeIdCompatibleVtableSummaryRecord(NameVals, StrtabBuilder, S.first,
>> + S.second, VE);
>> + Stream.EmitRecord(bitc::FS_TYPE_ID_METADATA, NameVals,
>> + TypeIdCompatibleVtableAbbrev);
>> + NameVals.clear();
>> + }
>> + }
>> +
>> Stream.ExitBlock();
>> }
>>
>>
>> Modified: llvm/trunk/lib/IR/AsmWriter.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/IR/AsmWriter.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/IR/AsmWriter.cpp (original)
>> +++ llvm/trunk/lib/IR/AsmWriter.cpp Tue Jul 2 12:38:02 2019
>> @@ -1037,6 +1037,9 @@ void SlotTracker::processIndex() {
>> TidIter != TheIndex->typeIds().end(); TidIter++)
>> CreateTypeIdSlot(TidIter->second.first);
>>
>> + for (auto &TId : TheIndex->typeIdCompatibleVtableMap())
>> + CreateGUIDSlot(GlobalValue::getGUID(TId.first));
>> +
>> ST_DEBUG("end processIndex!\n");
>> }
>>
>> @@ -2410,6 +2413,7 @@ public:
>> void printGlobalVarSummary(const GlobalVarSummary *GS);
>> void printFunctionSummary(const FunctionSummary *FS);
>> void printTypeIdSummary(const TypeIdSummary &TIS);
>> + void printTypeIdCompatibleVtableSummary(const TypeIdCompatibleVtableInfo &TI);
>> void printTypeTestResolution(const TypeTestResolution &TTRes);
>> void printArgs(const std::vector<uint64_t> &Args);
>> void printWPDRes(const WholeProgramDevirtResolution &WPDRes);
>> @@ -2712,6 +2716,15 @@ void AssemblyWriter::printModuleSummaryI
>> printTypeIdSummary(TidIter->second.second);
>> Out << ") ; guid = " << TidIter->first << "\n";
>> }
>> +
>> + // Print the TypeIdCompatibleVtableMap entries.
>> + for (auto &TId : TheIndex->typeIdCompatibleVtableMap()) {
>> + auto GUID = GlobalValue::getGUID(TId.first);
>> + Out << "^" << Machine.getGUIDSlot(GUID)
>> + << " = typeidCompatibleVTable: (name: \"" << TId.first << "\"";
>> + printTypeIdCompatibleVtableSummary(TId.second);
>> + Out << ") ; guid = " << GUID << "\n";
>> + }
>> }
>>
>> static const char *
>> @@ -2794,6 +2807,19 @@ void AssemblyWriter::printTypeIdSummary(
>> Out << ")";
>> }
>>
>> +void AssemblyWriter::printTypeIdCompatibleVtableSummary(
>> + const TypeIdCompatibleVtableInfo &TI) {
>> + Out << ", summary: (";
>> + FieldSeparator FS;
>> + for (auto &P : TI) {
>> + Out << FS;
>> + Out << "(offset: " << P.AddressPointOffset << ", ";
>> + Out << "^" << Machine.getGUIDSlot(P.VTableVI.getGUID());
>> + Out << ")";
>> + }
>> + Out << ")";
>> +}
>> +
>> void AssemblyWriter::printArgs(const std::vector<uint64_t> &Args) {
>> Out << "args: (";
>> FieldSeparator FS;
>> @@ -2863,6 +2889,19 @@ void AssemblyWriter::printAliasSummary(c
>>
>> void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) {
>> Out << ", varFlags: (readonly: " << GS->VarFlags.ReadOnly << ")";
>> +
>> + auto VTableFuncs = GS->vTableFuncs();
>> + if (!VTableFuncs.empty()) {
>> + Out << ", vTableFuncs: (";
>> + FieldSeparator FS;
>> + for (auto &P : VTableFuncs) {
>> + Out << FS;
>> + Out << "(virtFunc: ^" << Machine.getGUIDSlot(P.FuncVI.getGUID())
>> + << ", offset: " << P.VTableOffset;
>> + Out << ")";
>> + }
>> + Out << ")";
>> + }
>> }
>>
>> static std::string getLinkageName(GlobalValue::LinkageTypes LT) {
>>
>> Modified: llvm/trunk/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp (original)
>> +++ llvm/trunk/lib/Transforms/IPO/ThinLTOBitcodeWriter.cpp Tue Jul 2 12:38:02 2019
>> @@ -417,34 +417,53 @@ void splitAndWriteThinLTOBitcode(
>> }
>> }
>>
>> -// Returns whether this module needs to be split because splitting is
>> -// enabled and it uses type metadata.
>> -bool requiresSplit(Module &M) {
>> - // First check if the LTO Unit splitting has been enabled.
>> +// Check if the LTO Unit splitting has been enabled.
>> +bool enableSplitLTOUnit(Module &M) {
>> bool EnableSplitLTOUnit = false;
>> if (auto *MD = mdconst::extract_or_null<ConstantInt>(
>> M.getModuleFlag("EnableSplitLTOUnit")))
>> EnableSplitLTOUnit = MD->getZExtValue();
>> - if (!EnableSplitLTOUnit)
>> - return false;
>> + return EnableSplitLTOUnit;
>> +}
>>
>> - // Module only needs to be split if it contains type metadata.
>> +// Returns whether this module needs to be split because it uses type metadata.
>> +bool hasTypeMetadata(Module &M) {
>> for (auto &GO : M.global_objects()) {
>> if (GO.hasMetadata(LLVMContext::MD_type))
>> return true;
>> }
>> -
>> return false;
>> }
>>
>> void writeThinLTOBitcode(raw_ostream &OS, raw_ostream *ThinLinkOS,
>> function_ref<AAResults &(Function &)> AARGetter,
>> Module &M, const ModuleSummaryIndex *Index) {
>> - // Split module if splitting is enabled and it contains any type metadata.
>> - if (requiresSplit(M))
>> - return splitAndWriteThinLTOBitcode(OS, ThinLinkOS, AARGetter, M);
>> + std::unique_ptr<ModuleSummaryIndex> NewIndex = nullptr;
>> + // See if this module has any type metadata. If so, we try to split it
>> + // or at least promote type ids to enable WPD.
>> + if (hasTypeMetadata(M)) {
>> + if (enableSplitLTOUnit(M))
>> + return splitAndWriteThinLTOBitcode(OS, ThinLinkOS, AARGetter, M);
>> + // Promote type ids as needed for index-based WPD.
>> + std::string ModuleId = getUniqueModuleId(&M);
>> + if (!ModuleId.empty()) {
>> + promoteTypeIds(M, ModuleId);
>> + // Need to rebuild the index so that it contains type metadata
>> + // for the newly promoted type ids.
>> + // FIXME: Probably should not bother building the index at all
>> + // in the caller of writeThinLTOBitcode (which does so via the
>> + // ModuleSummaryIndexAnalysis pass), since we have to rebuild it
>> + // anyway whenever there is type metadata (here or in
>> + // splitAndWriteThinLTOBitcode). Just always build it once via the
>> + // buildModuleSummaryIndex when Module(s) are ready.
>> + ProfileSummaryInfo PSI(M);
>> + NewIndex = llvm::make_unique<ModuleSummaryIndex>(
>> + buildModuleSummaryIndex(M, nullptr, &PSI));
>> + Index = NewIndex.get();
>> + }
>> + }
>>
>> - // Otherwise we can just write it out as a regular module.
>> + // Write it out as an unsplit ThinLTO module.
>>
>> // Save the module hash produced for the full bitcode, which will
>> // be used in the backends, and use that in the minimized bitcode
>>
>> Added: llvm/trunk/test/Assembler/thinlto-vtable-summary.ll
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/Assembler/thinlto-vtable-summary.ll?rev=364960&view=auto
>> ==============================================================================
>> --- llvm/trunk/test/Assembler/thinlto-vtable-summary.ll (added)
>> +++ llvm/trunk/test/Assembler/thinlto-vtable-summary.ll Tue Jul 2 12:38:02 2019
>> @@ -0,0 +1,38 @@
>> +; Test summary parsing of index-based WPD related summary fields
>> +; RUN: llvm-as %s -o - | llvm-dis -o %t.ll
>> +; RUN: grep "^\^" %s >%t2
>> +; RUN: grep "^\^" %t.ll >%t3
>> +; Expect that the summary information is the same after round-trip through
>> +; llvm-as and llvm-dis.
>> +; RUN: diff %t2 %t3
>> +
>> +source_filename = "thinlto-vtable-summary.ll"
>> +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
>> +target triple = "x86_64-grtev4-linux-gnu"
>> +
>> +%struct.A = type { i32 (...)** }
>> +%struct.B = type { %struct.A }
>> +%struct.C = type { %struct.A }
>> +
>> + at _ZTV1B = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !1
>> + at _ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2
>> +
>> +declare i32 @_ZN1B1fEi(%struct.B*, i32)
>> +
>> +declare i32 @_ZN1A1nEi(%struct.A*, i32)
>> +
>> +declare i32 @_ZN1C1fEi(%struct.C*, i32)
>> +
>> +!0 = !{i64 16, !"_ZTS1A"}
>> +!1 = !{i64 16, !"_ZTS1B"}
>> +!2 = !{i64 16, !"_ZTS1C"}
>> +
>> +^0 = module: (path: "<stdin>", hash: (0, 0, 0, 0, 0))
>> +^1 = gv: (name: "_ZN1A1nEi") ; guid = 1621563287929432257
>> +^2 = gv: (name: "_ZTV1B", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0), vTableFuncs: ((virtFunc: ^3, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^3, ^1)))) ; guid = 5283576821522790367
>> +^3 = gv: (name: "_ZN1B1fEi") ; guid = 7162046368816414394
>> +^4 = gv: (name: "_ZTV1C", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0), vTableFuncs: ((virtFunc: ^5, offset: 16), (virtFunc: ^1, offset: 24)), refs: (^1, ^5)))) ; guid = 13624023785555846296
>> +^5 = gv: (name: "_ZN1C1fEi") ; guid = 14876272565662207556
>> +^6 = typeidCompatibleVTable: (name: "_ZTS1A", summary: ((offset: 16, ^2), (offset: 16, ^4))) ; guid = 7004155349499253778
>> +^7 = typeidCompatibleVTable: (name: "_ZTS1B", summary: ((offset: 16, ^2))) ; guid = 6203814149063363976
>> +^8 = typeidCompatibleVTable: (name: "_ZTS1C", summary: ((offset: 16, ^4))) ; guid = 1884921850105019584
>>
>> Added: llvm/trunk/test/ThinLTO/X86/devirt.ll
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/test/ThinLTO/X86/devirt.ll?rev=364960&view=auto
>> ==============================================================================
>> --- llvm/trunk/test/ThinLTO/X86/devirt.ll (added)
>> +++ llvm/trunk/test/ThinLTO/X86/devirt.ll Tue Jul 2 12:38:02 2019
>> @@ -0,0 +1,146 @@
>> +; REQUIRES: x86-registered-target
>> +
>> +; Test devirtualization through the thin link and backend.
>> +
>> +; Generate split module with summary for hybrid Thin/Regular LTO WPD.
>> +; RUN: opt -thinlto-bc -thinlto-split-lto-unit -o %t.o %s
>> +
>> +; Check that we have module flag showing splitting enabled, and that we don't
>> +; generate summary information needed for index-based WPD.
>> +; RUN: llvm-modextract -b -n=0 %t.o -o %t.o.0
>> +; RUN: llvm-dis -o - %t.o.0 | FileCheck %s --check-prefix=ENABLESPLITFLAG --implicit-check-not=vTableFuncs --implicit-check-not=typeidCompatibleVTable
>> +; RUN: llvm-modextract -b -n=1 %t.o -o %t.o.1
>> +; RUN: llvm-dis -o - %t.o.1 | FileCheck %s --check-prefix=ENABLESPLITFLAG --implicit-check-not=vTableFuncs --implicit-check-not=typeidCompatibleVTable
>> +; ENABLESPLITFLAG: !{i32 1, !"EnableSplitLTOUnit", i32 1}
>> +
>> +; Generate unsplit module with summary for ThinLTO index-based WPD.
>> +; RUN: opt -thinlto-bc -o %t2.o %s
>> +
>> +; Check that we don't have module flag when splitting not enabled for ThinLTO,
>> +; and that we generate summary information needed for index-based WPD.
>> +; RUN: llvm-dis -o - %t2.o | FileCheck %s --check-prefix=NOENABLESPLITFLAG
>> +; NOENABLESPLITFLAG-DAG: !{i32 1, !"EnableSplitLTOUnit", i32 0}
>> +; NOENABLESPLITFLAG-DAG: [[An:\^[0-9]+]] = gv: (name: "_ZN1A1nEi")
>> +; NOENABLESPLITFLAG-DAG: [[Bf:\^[0-9]+]] = gv: (name: "_ZN1B1fEi")
>> +; NOENABLESPLITFLAG-DAG: [[Cf:\^[0-9]+]] = gv: (name: "_ZN1C1fEi")
>> +; NOENABLESPLITFLAG-DAG: [[Dm:\^[0-9]+]] = gv: (name: "_ZN1D1mEi")
>> +; NOENABLESPLITFLAG-DAG: [[B:\^[0-9]+]] = gv: (name: "_ZTV1B", {{.*}} vTableFuncs: ((virtFunc: [[Bf]], offset: 16), (virtFunc: [[An]], offset: 24)), refs: ([[Bf]], [[An]])
>> +; NOENABLESPLITFLAG-DAG: [[C:\^[0-9]+]] = gv: (name: "_ZTV1C", {{.*}} vTableFuncs: ((virtFunc: [[Cf]], offset: 16), (virtFunc: [[An]], offset: 24)), refs: ([[An]], [[Cf]])
>> +; NOENABLESPLITFLAG-DAG: [[D:\^[0-9]+]] = gv: (name: "_ZTV1D", {{.*}} vTableFuncs: ((virtFunc: [[Dm]], offset: 16)), refs: ([[Dm]])
>> +; NOENABLESPLITFLAG-DAG: typeidCompatibleVTable: (name: "_ZTS1A", summary: ((offset: 16, [[B]]), (offset: 16, [[C]])))
>> +; NOENABLESPLITFLAG-DAG: typeidCompatibleVTable: (name: "_ZTS1B", summary: ((offset: 16, [[B]])))
>> +; NOENABLESPLITFLAG-DAG: typeidCompatibleVTable: (name: "_ZTS1C", summary: ((offset: 16, [[C]])))
>> +; Type Id on _ZTV1D should have been promoted
>> +; NOENABLESPLITFLAG-DAG: typeidCompatibleVTable: (name: "1${{.*}}", summary: ((offset: 16, [[D]])))
>> +
>> +; TODO: Test index-based WPD one %t2.o once implemented.
>> +
>> +; Legacy PM
>> +; RUN: llvm-lto2 run %t.o -save-temps -pass-remarks=. \
>> +; RUN: -o %t3 \
>> +; RUN: -r=%t.o,test,px \
>> +; RUN: -r=%t.o,_ZN1A1nEi,p \
>> +; RUN: -r=%t.o,_ZN1B1fEi,p \
>> +; RUN: -r=%t.o,_ZN1C1fEi,p \
>> +; RUN: -r=%t.o,_ZN1D1mEi,p \
>> +; RUN: -r=%t.o,_ZTV1B, \
>> +; RUN: -r=%t.o,_ZTV1C, \
>> +; RUN: -r=%t.o,_ZTV1D, \
>> +; RUN: -r=%t.o,_ZN1A1nEi, \
>> +; RUN: -r=%t.o,_ZN1B1fEi, \
>> +; RUN: -r=%t.o,_ZN1C1fEi, \
>> +; RUN: -r=%t.o,_ZN1D1mEi, \
>> +; RUN: -r=%t.o,_ZTV1B,px \
>> +; RUN: -r=%t.o,_ZTV1C,px \
>> +; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK
>> +; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
>> +
>> +; New PM
>> +; RUN: llvm-lto2 run %t.o -save-temps -use-new-pm -pass-remarks=. \
>> +; RUN: -o %t3 \
>> +; RUN: -r=%t.o,test,px \
>> +; RUN: -r=%t.o,_ZN1A1nEi,p \
>> +; RUN: -r=%t.o,_ZN1B1fEi,p \
>> +; RUN: -r=%t.o,_ZN1C1fEi,p \
>> +; RUN: -r=%t.o,_ZN1D1mEi,p \
>> +; RUN: -r=%t.o,_ZTV1B, \
>> +; RUN: -r=%t.o,_ZTV1C, \
>> +; RUN: -r=%t.o,_ZTV1D, \
>> +; RUN: -r=%t.o,_ZN1A1nEi, \
>> +; RUN: -r=%t.o,_ZN1B1fEi, \
>> +; RUN: -r=%t.o,_ZN1C1fEi, \
>> +; RUN: -r=%t.o,_ZN1D1mEi, \
>> +; RUN: -r=%t.o,_ZTV1B,px \
>> +; RUN: -r=%t.o,_ZTV1C,px \
>> +; RUN: -r=%t.o,_ZTV1D,px 2>&1 | FileCheck %s --check-prefix=REMARK
>> +; RUN: llvm-dis %t3.1.4.opt.bc -o - | FileCheck %s --check-prefix=CHECK-IR
>> +
>> +; REMARK-DAG: single-impl: devirtualized a call to _ZN1A1nEi
>> +; REMARK-DAG: single-impl: devirtualized a call to _ZN1D1mEi
>> +
>> +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
>> +target triple = "x86_64-grtev4-linux-gnu"
>> +
>> +%struct.A = type { i32 (...)** }
>> +%struct.B = type { %struct.A }
>> +%struct.C = type { %struct.A }
>> +%struct.D = type { i32 (...)** }
>> +
>> + at _ZTV1B = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.B*, i32)* @_ZN1B1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !1
>> + at _ZTV1C = constant { [4 x i8*] } { [4 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.C*, i32)* @_ZN1C1fEi to i8*), i8* bitcast (i32 (%struct.A*, i32)* @_ZN1A1nEi to i8*)] }, !type !0, !type !2
>> + at _ZTV1D = constant { [3 x i8*] } { [3 x i8*] [i8* null, i8* undef, i8* bitcast (i32 (%struct.D*, i32)* @_ZN1D1mEi to i8*)] }, !type !3
>> +
>> +
>> +; CHECK-IR-LABEL: define i32 @test
>> +define i32 @test(%struct.A* %obj, %struct.D* %obj2, i32 %a) {
>> +entry:
>> + %0 = bitcast %struct.A* %obj to i8***
>> + %vtable = load i8**, i8*** %0
>> + %1 = bitcast i8** %vtable to i8*
>> + %p = call i1 @llvm.type.test(i8* %1, metadata !"_ZTS1A")
>> + call void @llvm.assume(i1 %p)
>> + %fptrptr = getelementptr i8*, i8** %vtable, i32 1
>> + %2 = bitcast i8** %fptrptr to i32 (%struct.A*, i32)**
>> + %fptr1 = load i32 (%struct.A*, i32)*, i32 (%struct.A*, i32)** %2, align 8
>> +
>> + ; Check that the call was devirtualized.
>> + ; CHECK-IR: %call = tail call i32 @_ZN1A1nEi
>> + %call = tail call i32 %fptr1(%struct.A* nonnull %obj, i32 %a)
>> +
>> + %3 = bitcast i8** %vtable to i32 (%struct.A*, i32)**
>> + %fptr22 = load i32 (%struct.A*, i32)*, i32 (%struct.A*, i32)** %3, align 8
>> +
>> + ; We still have to call it as virtual.
>> + ; CHECK-IR: %call3 = tail call i32 %fptr22
>> + %call3 = tail call i32 %fptr22(%struct.A* nonnull %obj, i32 %call)
>> +
>> + %4 = bitcast %struct.D* %obj2 to i8***
>> + %vtable2 = load i8**, i8*** %4
>> + %5 = bitcast i8** %vtable2 to i8*
>> + %p2 = call i1 @llvm.type.test(i8* %5, metadata !4)
>> + call void @llvm.assume(i1 %p2)
>> +
>> + %6 = bitcast i8** %vtable2 to i32 (%struct.D*, i32)**
>> + %fptr33 = load i32 (%struct.D*, i32)*, i32 (%struct.D*, i32)** %6, align 8
>> +
>> + ; Check that the call was devirtualized.
>> + ; CHECK-IR: %call4 = tail call i32 @_ZN1D1mEi
>> + %call4 = tail call i32 %fptr33(%struct.D* nonnull %obj2, i32 %call3)
>> + ret i32 %call4
>> +}
>> +; CHECK-IR-LABEL: ret i32
>> +; CHECK-IR-LABEL: }
>> +
>> +declare i1 @llvm.type.test(i8*, metadata)
>> +declare void @llvm.assume(i1)
>> +
>> +declare i32 @_ZN1B1fEi(%struct.B* %this, i32 %a)
>> +declare i32 @_ZN1A1nEi(%struct.A* %this, i32 %a)
>> +declare i32 @_ZN1C1fEi(%struct.C* %this, i32 %a)
>> +declare i32 @_ZN1D1mEi(%struct.D* %this, i32 %a)
>> +
>> +!0 = !{i64 16, !"_ZTS1A"}
>> +!1 = !{i64 16, !"_ZTS1B"}
>> +!2 = !{i64 16, !"_ZTS1C"}
>> +!3 = !{i64 16, !4}
>> +!4 = distinct !{}
>>
>> Modified: llvm/trunk/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp
>> URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp?rev=364960&r1=364959&r2=364960&view=diff
>> ==============================================================================
>> --- llvm/trunk/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp (original)
>> +++ llvm/trunk/tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp Tue Jul 2 12:38:02 2019
>> @@ -317,6 +317,7 @@ static const char *GetCodeName(unsigned
>> STRINGIFY_CODE(FS, PERMODULE_PROFILE)
>> STRINGIFY_CODE(FS, PERMODULE_RELBF)
>> STRINGIFY_CODE(FS, PERMODULE_GLOBALVAR_INIT_REFS)
>> + STRINGIFY_CODE(FS, PERMODULE_VTABLE_GLOBALVAR_INIT_REFS)
>> STRINGIFY_CODE(FS, COMBINED)
>> STRINGIFY_CODE(FS, COMBINED_PROFILE)
>> STRINGIFY_CODE(FS, COMBINED_GLOBALVAR_INIT_REFS)
>> @@ -334,6 +335,7 @@ static const char *GetCodeName(unsigned
>> STRINGIFY_CODE(FS, CFI_FUNCTION_DEFS)
>> STRINGIFY_CODE(FS, CFI_FUNCTION_DECLS)
>> STRINGIFY_CODE(FS, TYPE_ID)
>> + STRINGIFY_CODE(FS, TYPE_ID_METADATA)
>> }
>> case bitc::METADATA_ATTACHMENT_ID:
>> switch(CodeID) {
>>
>>
>> _______________________________________________
>> llvm-commits mailing list
>> llvm-commits at lists.llvm.org
>> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>>
> _______________________________________________
> llvm-commits mailing list
> llvm-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-commits
>
More information about the llvm-commits
mailing list