Index: llvm/include/llvm/IR/ModuleSummaryIndex.h =================================================================== --- llvm/include/llvm/IR/ModuleSummaryIndex.h +++ llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -793,6 +793,8 @@ return *VTableFuncs; return {}; } + + size_t numConstantRefs() const; }; struct TypeTestResolution { Index: llvm/lib/Analysis/ModuleSummaryAnalysis.cpp =================================================================== --- llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -239,6 +239,33 @@ return false; } +static void markRefsToConstantsAsReadOnly(std::vector &Refs, + size_t Count) { + auto IsConstantValue = [](const GlobalValue *GV) { + if (const auto *GVar = dyn_cast(GV)) + return GVar->isConstant(); + + if (const auto *GA = dyn_cast(GV)) + if (const auto *GVar = dyn_cast(GA->getAliasee())) + return GVar->isConstant(); + return false; + }; + + size_t NumConstRefs = 0; + // Detect readonly references and group them at the end of + // Refs array. + for (size_t N = 0; N < Count && !Refs[N].isReadOnly();) { + if (!IsConstantValue(Refs[N].getValue())) { + ++N; + continue; + } + + Refs[N].setReadOnly(); + std::swap(Refs[N], Refs[Count - NumConstRefs - 1]); + ++NumConstRefs; + } +} + static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT, @@ -427,7 +454,7 @@ if (LoadRefEdges.remove(VI)) RefEdges.insert(VI); - unsigned RefCnt = RefEdges.size(); + unsigned RefCnt = RefEdges.size(), UntypedRefCnt = RefCnt; // All new reference edges inserted in two loops below are either // read or write only. They will be grouped in the end of RefEdges // vector, so we can use a single integer value to identify them. @@ -444,6 +471,9 @@ for (; RefCnt < Refs.size(); ++RefCnt) Refs[RefCnt].setWriteOnly(); + + // Mark all references to constant objects as readonly. + markRefsToConstantsAsReadOnly(Refs, UntypedRefCnt); } else { Refs = RefEdges.takeVector(); } @@ -599,9 +629,10 @@ bool CanBeInternalized = !V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() && !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass(); + auto Refs = RefEdges.takeVector(); + markRefsToConstantsAsReadOnly(Refs, Refs.size()); GlobalVarSummary::GVarFlags VarFlags(CanBeInternalized, CanBeInternalized); - auto GVarSummary = std::make_unique(Flags, VarFlags, - RefEdges.takeVector()); + auto GVarSummary = std::make_unique(Flags, VarFlags, Refs); if (NonRenamableLocal) CantBePromoted.insert(V.getGUID()); if (HasBlockAddress) Index: llvm/lib/Bitcode/Reader/BitcodeReader.cpp =================================================================== --- llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -5778,9 +5778,9 @@ } const uint64_t Version = Record[0]; const bool IsOldProfileFormat = Version == 1; - if (Version < 1 || Version > 8) + if (Version < 1 || Version > 9) return error("Invalid summary version " + Twine(Version) + - ". Version should be in the range [1-7]."); + ". Version should be in the range [1-9]."); Record.clear(); // Keep around the last seen summary to be used when we see an optional @@ -5964,12 +5964,18 @@ GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, /* WriteOnly */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); + size_t NumConstRefs = 0; if (Version >= 5) { GVF = getDecodedGVarFlags(Record[2]); RefArrayStart = 3; + if (Version >= 9) { + NumConstRefs = Record[3]; + RefArrayStart = 4; + } } std::vector Refs = makeRefList(ArrayRef(Record).slice(RefArrayStart)); + setSpecialRefs(Refs, NumConstRefs, 0); auto FS = std::make_unique(Flags, GVF, std::move(Refs)); FS->setModulePath(getThisModule()->first()); @@ -5985,12 +5991,17 @@ unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; GlobalVarSummary::GVarFlags GVF = getDecodedGVarFlags(Record[2]); - unsigned NumRefs = Record[3]; + unsigned NumRefs = Record[3], NumConstRefs = 0; unsigned RefListStartIndex = 4; + if (Version >= 9) { + NumConstRefs = Record[4]; + RefListStartIndex = 5; + } unsigned VTableListStartIndex = RefListStartIndex + NumRefs; auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); std::vector Refs = makeRefList( ArrayRef(Record).slice(RefListStartIndex, NumRefs)); + setSpecialRefs(Refs, NumConstRefs, 0); VTableFuncList VTableFuncs; for (unsigned I = VTableListStartIndex, E = Record.size(); I != E; ++I) { ValueInfo Callee = getValueInfoFromValueId(Record[I]).first; @@ -6100,12 +6111,18 @@ GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, /* WriteOnly */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); + size_t NumConstRefs = 0; if (Version >= 5) { GVF = getDecodedGVarFlags(Record[3]); RefArrayStart = 4; + if (Version >= 9) { + NumConstRefs = Record[4]; + RefArrayStart = 5; + } } std::vector Refs = makeRefList(ArrayRef(Record).slice(RefArrayStart)); + setSpecialRefs(Refs, NumConstRefs, 0); auto FS = std::make_unique(Flags, GVF, std::move(Refs)); LastSeenSummary = FS.get(); Index: llvm/lib/Bitcode/Writer/BitcodeWriter.cpp =================================================================== --- llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -3702,12 +3702,9 @@ if (!VTableFuncs.empty()) NameVals.push_back(VS->refs().size()); - unsigned SizeBeforeRefs = NameVals.size(); + NameVals.push_back(VS->numConstantRefs()); for (auto &RI : VS->refs()) NameVals.push_back(VE.getValueID(RI.getValue())); - // Sort the refs for determinism output, the vector returned by FS->refs() has - // been initialized from a DenseSet. - llvm::sort(NameVals.begin() + SizeBeforeRefs, NameVals.end()); if (VTableFuncs.empty()) Stream.EmitRecord(bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS, NameVals, @@ -3728,7 +3725,7 @@ // Current version for the summary. // This is bumped whenever we introduce changes in the way some record are // interpreted, like flags for instance. -static const uint64_t INDEX_VERSION = 8; +static const uint64_t INDEX_VERSION = 9; /// Emit the per-module summary section alongside the rest of /// the module's bitcode. @@ -3800,6 +3797,8 @@ Abbv->Add(BitCodeAbbrevOp(bitc::FS_PERMODULE_GLOBALVAR_INIT_REFS)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 2)); // varFlags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numConstantRefs Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); // valueids Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); unsigned FSModRefsAbbrev = Stream.EmitAbbrev(std::move(Abbv)); @@ -3809,7 +3808,9 @@ 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, 2)); // varFlags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numConstantRefs // numrefs x valueid, n x (valueid , offset) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3953,6 +3954,8 @@ Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // valueid Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // modid Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // flags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 2)); // varFlags + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numConstantRefs Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); // valueids Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); unsigned FSModRefsAbbrev = Stream.EmitAbbrev(std::move(Abbv)); @@ -4019,6 +4022,7 @@ NameVals.push_back(Index.getModuleId(VS->modulePath())); NameVals.push_back(getEncodedGVSummaryFlags(VS->flags())); NameVals.push_back(getEncodedGVarFlags(VS->varflags())); + NameVals.push_back(VS->numConstantRefs()); for (auto &RI : VS->refs()) { auto RefValueId = getValueId(RI.getGUID()); if (!RefValueId) Index: llvm/lib/IR/ModuleSummaryIndex.cpp =================================================================== --- llvm/lib/IR/ModuleSummaryIndex.cpp +++ llvm/lib/IR/ModuleSummaryIndex.cpp @@ -47,6 +47,15 @@ }); } +size_t GlobalVarSummary::numConstantRefs() const { + // Constant references are grouped in the end of refs() array. + int N; + auto Refs = refs(); + for (N = Refs.size() - 1; N >= 0 && Refs[N].isReadOnly(); --N) { + } + return Refs.size() - N - 1; +} + // Gets the number of readonly and writeonly refs in RefEdgeList std::pair FunctionSummary::specialRefCounts() const { // Here we take advantage of having all readonly and writeonly references @@ -107,14 +116,13 @@ static void propagateAttributesToRefs(GlobalValueSummary *S) { // If reference is not readonly or writeonly then referenced summary is not // read/writeonly either. Note that: - // - All references from GlobalVarSummary are conservatively considered as - // not readonly or writeonly. Tracking them properly requires more complex - // analysis then we have now. + // - References from GlobalVarSummary can be readonly if they point to + // constant objects. Those can't be writeonly. // // - AliasSummary objects have no refs at all so this function is a no-op // for them. for (auto &VI : S->refs()) { - assert(VI.getAccessSpecifier() == 0 || isa(S)); + assert(!VI.isWriteOnly() || isa(S)); for (auto &Ref : VI.getSummaryList()) // If references to alias is not read/writeonly then aliasee // is not read/writeonly Index: llvm/test/Assembler/thinlto-summary.ll =================================================================== --- llvm/test/Assembler/thinlto-summary.ll +++ llvm/test/Assembler/thinlto-summary.ll @@ -24,7 +24,7 @@ ^8 = gv: (guid: 7, summaries: (function: (module: ^0, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) ^9 = gv: (guid: 8, summaries: (function: (module: ^0, flags: (linkage: weak_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 1), insts: 1))) ^10 = gv: (guid: 9, summaries: (function: (module: ^0, flags: (linkage: weak, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 1))) -^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0)))) +^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0), refs: (readonly ^13, ^14)))) ; Test appending globel variable with reference (tests backward reference on ; refs). ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 0), refs: (^4)))) @@ -76,7 +76,7 @@ ; CHECK: ^8 = gv: (guid: 7, summaries: (function: (module: ^0, flags: (linkage: linkonce_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1))) ; CHECK: ^9 = gv: (guid: 8, summaries: (function: (module: ^0, flags: (linkage: weak_odr, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 1), insts: 1))) ; CHECK: ^10 = gv: (guid: 9, summaries: (function: (module: ^0, flags: (linkage: weak, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1))) -; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0)))) +; CHECK: ^11 = gv: (guid: 10, summaries: (variable: (module: ^0, flags: (linkage: common, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0), refs: (^14, readonly ^13)))) ; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0), refs: (^4)))) ; CHECK: ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 0)))) ; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 0)))) Index: llvm/test/Bitcode/summary_version.ll =================================================================== --- llvm/test/Bitcode/summary_version.ll +++ llvm/test/Bitcode/summary_version.ll @@ -2,7 +2,7 @@ ; RUN: opt -module-summary %s -o - | llvm-bcanalyzer -dump | FileCheck %s ; CHECK: +; CHECK: Index: llvm/test/Bitcode/thinlto-function-summary-refgraph.ll =================================================================== --- llvm/test/Bitcode/thinlto-function-summary-refgraph.ll +++ llvm/test/Bitcode/thinlto-function-summary-refgraph.ll @@ -61,7 +61,7 @@ ; CHECK-DAG: ; Variable bar initialization contains address reference to func: ; op0=bar op2=func -; CHECK-DAG: +; CHECK-DAG: ; CHECK: ; CHECK: