Index: include/llvm/Bitcode/LLVMBitCodes.h =================================================================== --- include/llvm/Bitcode/LLVMBitCodes.h +++ include/llvm/Bitcode/LLVMBitCodes.h @@ -268,6 +268,8 @@ // n x (typeid, kind, name, numrba, // numrba x (numarg, numarg x arg, kind, info, byte, bit))] FS_TYPE_ID = 21, + // List of function constant references + FS_CONST_REF_LIST = 22, }; enum MetadataCodes { Index: include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- include/llvm/IR/ModuleSummaryIndex.h +++ include/llvm/IR/ModuleSummaryIndex.h @@ -17,6 +17,7 @@ #define LLVM_IR_MODULESUMMARYINDEX_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" @@ -500,6 +501,10 @@ /// List of call edge pairs from this function. std::vector CallGraphEdgeList; + /// Bit vector, where each bit corresponds to a single outgoing reference. + /// Value of 1 means read-only access to referenced valuec. + BitVector RefAccessBits; + std::unique_ptr TIdInfo; public: @@ -523,6 +528,14 @@ std::move(TypeCheckedLoadConstVCalls)}); } + const BitVector &getRefAccessBits() const { return RefAccessBits; } + void setRefAccessBits(BitVector &&BV) { + // We accept either empty bit vector (means all refs are RW) or + // one of size equal to number of references. + assert(BV.empty() || BV.size() == refs().size()); + RefAccessBits = std::move(BV); + } + /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { return GVS->getSummaryKind() == FunctionKind; Index: lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- lib/Analysis/ModuleSummaryAnalysis.cpp +++ lib/Analysis/ModuleSummaryAnalysis.cpp @@ -14,6 +14,7 @@ #include "llvm/Analysis/ModuleSummaryAnalysis.h" #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/BitVector.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/MapVector.h" #include "llvm/ADT/STLExtras.h" @@ -208,6 +209,38 @@ } } +static bool isNonVolatileLoad(const Instruction *I) { + if (const auto *LI = dyn_cast(I)) + return !LI->isVolatile(); + + return false; +} + +static void processInstructionRefs(SetVector &FuncRefs, + const Instruction *I, + SetVector &InstRefs, + DenseMap &RefAccessMap) { + for (auto &VI : InstRefs) { + bool NonVolatileLoad = isNonVolatileLoad(I); + auto P = RefAccessMap.insert({VI, NonVolatileLoad}); + if (!P.second && !NonVolatileLoad) + (*P.first).second = false; + + FuncRefs.insert(VI); + } +} + +static BitVector computeRefAccessBits(SetVector &Refs, + DenseMap &RefAccessMap) { + BitVector Res; + Res.resize(Refs.size()); + int I = 0; + + for (auto &VI : Refs) + Res[I++] = RefAccessMap[VI]; + return Res; +} + static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, @@ -233,6 +266,7 @@ // Add personality function, prefix data and prologue data to function's ref // list. findRefEdges(Index, &F, RefEdges, Visited); + DenseMap RefAccessMap; bool HasInlineAsmMaybeReferencingInternal = false; for (const BasicBlock &BB : F) @@ -240,7 +274,11 @@ if (isa(I)) continue; ++NumInsts; - findRefEdges(Index, &I, RefEdges, Visited); + + SetVector InstRefEdges; + findRefEdges(Index, &I, InstRefEdges, Visited); + processInstructionRefs(RefEdges, &I, InstRefEdges, RefAccessMap); + auto CS = ImmutableCallSite(&I); if (!CS) continue; @@ -347,6 +385,7 @@ F.getAttributes().hasFnAttribute(Attribute::NoInline); GlobalValueSummary::GVFlags Flags(F.getLinkage(), NotEligibleForImport, /* Live = */ false, F.isDSOLocal()); + BitVector RefAccessBits = computeRefAccessBits(RefEdges, RefAccessMap); FunctionSummary::FFlags FunFlags{ F.hasFnAttribute(Attribute::ReadNone), F.hasFnAttribute(Attribute::ReadOnly), @@ -361,6 +400,7 @@ TypeCheckedLoadConstVCalls.takeVector()); if (NonRenamableLocal) CantBePromoted.insert(F.getGUID()); + FuncSummary->setRefAccessBits(std::move(RefAccessBits)); Index.addGlobalValueSummary(F, std::move(FuncSummary)); } Index: lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- lib/Bitcode/Reader/BitcodeReader.cpp +++ lib/Bitcode/Reader/BitcodeReader.cpp @@ -5160,6 +5160,16 @@ parseWholeProgramDevirtResolution(Record, Strtab, Slot, TypeId); } +static BitVector computeRefAccessBits(const std::vector &RORefIDs, + size_t NumRefs) { + BitVector Res; + Res.resize(NumRefs); + + for (auto RefID : RORefIDs) + Res.set(RefID); + return Res; +} + // Eagerly parse the entire summary block. This populates the GlobalValueSummary // objects in the index. Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { @@ -5196,6 +5206,9 @@ std::vector PendingTypeTestAssumeConstVCalls, PendingTypeCheckedLoadConstVCalls; + // Contains numbers of RO references. + std::vector RORefIDs; + while (true) { BitstreamEntry Entry = Stream.advanceSkippingSubblocks(); @@ -5281,6 +5294,7 @@ std::vector Calls = makeCallList( ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, HasRelBF); + BitVector RefAccessBits = computeRefAccessBits(RORefIDs, Refs.size()); auto FS = llvm::make_unique( Flags, InstCount, getDecodedFFlags(RawFunFlags), std::move(Refs), std::move(Calls), std::move(PendingTypeTests), @@ -5293,9 +5307,11 @@ PendingTypeCheckedLoadVCalls.clear(); PendingTypeTestAssumeConstVCalls.clear(); PendingTypeCheckedLoadConstVCalls.clear(); + RORefIDs.clear(); auto VIAndOriginalGUID = getValueInfoFromValueId(ValueID); FS->setModulePath(getThisModule()->first()); FS->setOriginalName(VIAndOriginalGUID.second); + FS->setRefAccessBits(std::move(RefAccessBits)); TheIndex.addGlobalValueSummary(VIAndOriginalGUID.first, std::move(FS)); break; } @@ -5374,6 +5390,7 @@ ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, false); ValueInfo VI = getValueInfoFromValueId(ValueID).first; + BitVector RefAccessBits = computeRefAccessBits(RORefIDs, Refs.size()); auto FS = llvm::make_unique( Flags, InstCount, getDecodedFFlags(RawFunFlags), std::move(Refs), std::move(Edges), std::move(PendingTypeTests), @@ -5386,9 +5403,11 @@ PendingTypeCheckedLoadVCalls.clear(); PendingTypeTestAssumeConstVCalls.clear(); PendingTypeCheckedLoadConstVCalls.clear(); + RORefIDs.clear(); LastSeenSummary = FS.get(); LastSeenGUID = VI.getGUID(); FS->setModulePath(ModuleIdMap[ModuleId]); + FS->setRefAccessBits(std::move(RefAccessBits)); TheIndex.addGlobalValueSummary(VI, std::move(FS)); break; } @@ -5445,6 +5464,11 @@ LastSeenGUID = 0; break; } + case bitc::FS_CONST_REF_LIST: + assert(RORefIDs.empty()); + RORefIDs.insert(RORefIDs.end(), Record.begin(), Record.end()); + break; + case bitc::FS_TYPE_TESTS: assert(PendingTypeTests.empty()); PendingTypeTests.insert(PendingTypeTests.end(), Record.begin(), Index: lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- lib/Bitcode/Writer/BitcodeWriter.cpp +++ lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3439,6 +3439,23 @@ W.second); } +static void writeFunctionRefAccess(BitstreamWriter &Stream, + GlobalValueSummary *Summary) { + SmallVector Record; + auto *FS = cast(Summary); + const BitVector &BV = FS->getRefAccessBits(); + + assert(BV.empty() || BV.size() == FS->refs().size()); + + // Unabbreviated records are 6 bits VBR encoded. To be more space efficient + // we emit numbers of read-only refs. + for (size_t I = 0; I < BV.size(); ++I) + if (BV[I]) + Record.push_back(I); + + Stream.EmitRecord(bitc::FS_CONST_REF_LIST, Record); +} + // Helper to emit a single function summary record. void ModuleBitcodeWriterBase::writePerModuleFunctionSummaryRecord( SmallVector &NameVals, GlobalValueSummary *Summary, @@ -3448,6 +3465,7 @@ FunctionSummary *FS = cast(Summary); writeFunctionTypeMetadataRecords(Stream, FS); + writeFunctionRefAccess(Stream, FS); NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); @@ -3754,6 +3772,7 @@ auto *FS = cast(S); writeFunctionTypeMetadataRecords(Stream, FS); + writeFunctionRefAccess(Stream, FS); NameVals.push_back(*ValueId); NameVals.push_back(Index.getModuleId(FS->modulePath())); @@ -3798,7 +3817,7 @@ continue; // The mapping from OriginalId to GUID may return a GUID // that corresponds to a static variable. Filter it out here. - // This can happen when + // This can happen when // 1) There is a call to a library function which does not have // a CallValidId; // 2) There is a static variable with the OriginalGUID identical Index: lib/IR/ModuleSummaryIndex.cpp =================================================================== --- lib/IR/ModuleSummaryIndex.cpp +++ lib/IR/ModuleSummaryIndex.cpp @@ -227,6 +227,19 @@ << "\"]; // defined externally\n"; } +static bool isReadOnlyRef(GlobalValueSummary *GVS, const ValueInfo &Ref) { + auto *FS = dyn_cast(GVS); + if (!FS) + return false; + + const BitVector &BV = FS->getRefAccessBits(); + if (BV.empty()) + return false; + + ArrayRef FuncRefs = FS->refs(); + return BV[&Ref - &FuncRefs[0]]; +} + void ModuleSummaryIndex::exportToDot(raw_ostream& OS) const { std::vector CrossModuleEdges; DenseMap> NodeMap; @@ -245,10 +258,11 @@ int DstMod, GlobalValue::GUID DstId, int TypeOrHotness) { // 0 corresponds to alias edge, 1 to ref edge, 2 to call with unknown // hotness, ... - TypeOrHotness += 2; + TypeOrHotness += 3; static const char *EdgeAttrs[] = { " [style=dotted]; // alias", " [style=dashed]; // ref", + " [style=dashed,color=forestgreen]; // const-ref", " // call (hotness : Unknown)", " [color=blue]; // call (hotness : Cold)", " // call (hotness : None)", @@ -308,13 +322,13 @@ for (auto &SummaryIt : GVSMap) { auto *GVS = SummaryIt.second; for (auto &R : GVS->refs()) - Draw(SummaryIt.first, R.getGUID(), -1); + Draw(SummaryIt.first, R.getGUID(), isReadOnlyRef(GVS, R) ? -1 : -2); if (auto *AS = dyn_cast_or_null(SummaryIt.second)) { auto AliaseeOrigId = AS->getAliasee().getOriginalName(); auto AliaseeId = getGUIDFromOriginalID(AliaseeOrigId); - Draw(SummaryIt.first, AliaseeId ? AliaseeId : AliaseeOrigId, -2); + Draw(SummaryIt.first, AliaseeId ? AliaseeId : AliaseeOrigId, -3); continue; } Index: test/ThinLTO/X86/distributed_indexes.ll =================================================================== --- test/ThinLTO/X86/distributed_indexes.ll +++ test/ThinLTO/X86/distributed_indexes.ll @@ -35,7 +35,9 @@ ; BACKEND2-DAG: ; BACKEND2-DAG: ; BACKEND2-DAG: +; BACKEND2-NEXT: M1_{{[0-9]+}} // call -; STRUCTURE-DAG: M0_{{[0-9]+}} -> M1_{{[0-9]+}} [{{.*}}]; // ref +; STRUCTURE-DAG: M0_{{[0-9]+}} -> M1_{{[0-9]+}} [{{.*}}]; // const-ref ; STRUCTURE-NEXT: } ; CLUSTER0: // Module: {{.*}}1.bc @@ -38,8 +38,8 @@ ; CLUSTER1-DAG: M1_[[B:[0-9]+]] [{{.*}}B|extern{{.*}}]; // variable ; CLUSTER1-DAG: M1_[[BAR:[0-9]+]] [{{.*}}bar|extern{{.*}}]; // function, dead ; CLUSTER1-NEXT: // Edges: -; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[B]] [{{.*}}]; // ref -; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[A]] [{{.*}}]; // ref +; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[B]] [{{.*}}]; // const-ref +; CLUSTER1-DAG: M1_[[FOO]] -> M1_[[A]] [{{.*}}]; // const-ref ; CLUSTER1-DAG: } target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" Index: tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp =================================================================== --- tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp +++ tools/llvm-bcanalyzer/llvm-bcanalyzer.cpp @@ -327,6 +327,7 @@ STRINGIFY_CODE(FS, CFI_FUNCTION_DEFS) STRINGIFY_CODE(FS, CFI_FUNCTION_DECLS) STRINGIFY_CODE(FS, TYPE_ID) + STRINGIFY_CODE(FS, CONST_REF_LIST) } case bitc::METADATA_ATTACHMENT_ID: switch(CodeID) { @@ -557,7 +558,7 @@ BitstreamEntry Entry = Stream.advance(BitstreamCursor::AF_DontAutoprocessAbbrevs); - + switch (Entry.Kind) { case BitstreamEntry::Error: return ReportError("malformed bitcode file"); @@ -573,7 +574,7 @@ } return false; } - + case BitstreamEntry::SubBlock: { uint64_t SubBlockBitStart = Stream.GetCurrentBitNo(); if (ParseBlock(Stream, BlockInfo, Entry.ID, IndentLevel + 1, @@ -581,7 +582,7 @@ return true; ++BlockStats.NumSubBlocks; uint64_t SubBlockBitEnd = Stream.GetCurrentBitNo(); - + // Don't include subblock sizes in the size of this block. BlockBitStart += SubBlockBitEnd-SubBlockBitStart; continue; @@ -596,7 +597,7 @@ ++BlockStats.NumAbbrevs; continue; } - + Record.clear(); ++BlockStats.NumRecords; @@ -727,7 +728,7 @@ if (BlobIsPrintable) outs() << "'" << Blob << "'"; else - outs() << "unprintable, " << Blob.size() << " bytes."; + outs() << "unprintable, " << Blob.size() << " bytes."; } }