diff options
author | Eugene Leviant <eleviant@accesssoftek.com> | 2019-07-05 15:25:05 +0000 |
---|---|---|
committer | Eugene Leviant <eleviant@accesssoftek.com> | 2019-07-05 15:25:05 +0000 |
commit | 3aef35288b5387d3b1e8c1627df5c031b9c151c7 (patch) | |
tree | a234bcaa1bd15f5170446186728e3fdd89a69159 | |
parent | 47afdaa4872e8617734e50bcc99bab149bfb08f8 (diff) | |
download | llvm-3aef35288b5387d3b1e8c1627df5c031b9c151c7.zip llvm-3aef35288b5387d3b1e8c1627df5c031b9c151c7.tar.gz llvm-3aef35288b5387d3b1e8c1627df5c031b9c151c7.tar.bz2 |
[ThinLTO] Attempt to recommit r365188 after alignment fix
llvm-svn: 365215
28 files changed, 595 insertions, 192 deletions
diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h index c931842..aacf8cf 100644 --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -119,7 +119,7 @@ class GlobalValueSummary; using GlobalValueSummaryList = std::vector<std::unique_ptr<GlobalValueSummary>>; -struct GlobalValueSummaryInfo { +struct LLVM_ALIGNAS(8) GlobalValueSummaryInfo { union NameOrGV { NameOrGV(bool HaveGVs) { if (HaveGVs) @@ -162,7 +162,8 @@ using GlobalValueSummaryMapTy = /// Struct that holds a reference to a particular GUID in a global value /// summary. struct ValueInfo { - PointerIntPair<const GlobalValueSummaryMapTy::value_type *, 2, int> + enum Flags { HaveGV = 1, ReadOnly = 2, WriteOnly = 4 }; + PointerIntPair<const GlobalValueSummaryMapTy::value_type *, 3, int> RefAndFlags; ValueInfo() = default; @@ -188,9 +189,33 @@ struct ValueInfo { : getRef()->second.U.Name; } - bool haveGVs() const { return RefAndFlags.getInt() & 0x1; } - bool isReadOnly() const { return RefAndFlags.getInt() & 0x2; } - void setReadOnly() { RefAndFlags.setInt(RefAndFlags.getInt() | 0x2); } + bool haveGVs() const { return RefAndFlags.getInt() & HaveGV; } + bool isReadOnly() const { + assert(isValidAccessSpecifier()); + return RefAndFlags.getInt() & ReadOnly; + } + bool isWriteOnly() const { + assert(isValidAccessSpecifier()); + return RefAndFlags.getInt() & WriteOnly; + } + unsigned getAccessSpecifier() const { + assert(isValidAccessSpecifier()); + return RefAndFlags.getInt() & (ReadOnly | WriteOnly); + } + bool isValidAccessSpecifier() const { + unsigned BadAccessMask = ReadOnly | WriteOnly; + return (RefAndFlags.getInt() & BadAccessMask) != BadAccessMask; + } + void setReadOnly() { + // We expect ro/wo attribute to set only once during + // ValueInfo lifetime. + assert(getAccessSpecifier() == 0); + RefAndFlags.setInt(RefAndFlags.getInt() | ReadOnly); + } + void setWriteOnly() { + assert(getAccessSpecifier() == 0); + RefAndFlags.setInt(RefAndFlags.getInt() | WriteOnly); + } const GlobalValueSummaryMapTy::value_type *getRef() const { return RefAndFlags.getPointer(); @@ -584,8 +609,8 @@ public: std::move(TypeTestAssumeConstVCalls), std::move(TypeCheckedLoadConstVCalls)}); } - // Gets the number of immutable refs in RefEdgeList - unsigned immutableRefCount() const; + // Gets the number of readonly and writeonly refs in RefEdgeList + std::pair<unsigned, unsigned> specialRefCounts() const; /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { @@ -713,9 +738,12 @@ using VTableFuncList = std::vector<VirtFuncOffset>; /// Global variable summary information to aid decisions and /// implementation of importing. /// -/// Global variable summary has extra flag, telling if it is -/// modified during the program run or not. This affects ThinLTO -/// internalization +/// Global variable summary has two extra flag, telling if it is +/// readonly or writeonly. Both readonly and writeonly variables +/// can be optimized in the backed: readonly variables can be +/// const-folded, while writeonly vars can be completely eliminated +/// together with corresponding stores. We let both things happen +/// by means of internalizing such variables after ThinLTO import. class GlobalVarSummary : public GlobalValueSummary { private: /// For vtable definitions this holds the list of functions and @@ -724,9 +752,14 @@ private: public: struct GVarFlags { - GVarFlags(bool ReadOnly = false) : ReadOnly(ReadOnly) {} - - unsigned ReadOnly : 1; + GVarFlags(bool ReadOnly, bool WriteOnly) + : MaybeReadOnly(ReadOnly), MaybeWriteOnly(WriteOnly) {} + + // In permodule summaries both MaybeReadOnly and MaybeWriteOnly + // bits are set, because attribute propagation occurs later on + // thin link phase. + unsigned MaybeReadOnly : 1; + unsigned MaybeWriteOnly : 1; } VarFlags; GlobalVarSummary(GVFlags Flags, GVarFlags VarFlags, @@ -740,8 +773,10 @@ public: } GVarFlags varflags() const { return VarFlags; } - void setReadOnly(bool RO) { VarFlags.ReadOnly = RO; } - bool isReadOnly() const { return VarFlags.ReadOnly; } + void setReadOnly(bool RO) { VarFlags.MaybeReadOnly = RO; } + void setWriteOnly(bool WO) { VarFlags.MaybeWriteOnly = WO; } + bool maybeReadOnly() const { return VarFlags.MaybeReadOnly; } + bool maybeWriteOnly() const { return VarFlags.MaybeWriteOnly; } void setVTableFuncs(VTableFuncList Funcs) { assert(!VTableFuncs); @@ -1312,7 +1347,7 @@ public: void dumpSCCs(raw_ostream &OS); /// Analyze index and detect unmodified globals - void propagateConstants(const DenseSet<GlobalValue::GUID> &PreservedSymbols); + void propagateAttributes(const DenseSet<GlobalValue::GUID> &PreservedSymbols); }; /// GraphTraits definition to build SCC for the index diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index 914561f..e25eb29 100644 --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -231,6 +231,13 @@ static bool isNonVolatileLoad(const Instruction *I) { return false; } +static bool isNonVolatileStore(const Instruction *I) { + if (const auto *SI = dyn_cast<StoreInst>(I)) + return !SI->isVolatile(); + + return false; +} + static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT, @@ -245,7 +252,7 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, // Map from callee ValueId to profile count. Used to accumulate profile // counts for all static calls to a given callee. MapVector<ValueInfo, CalleeInfo> CallGraphEdges; - SetVector<ValueInfo> RefEdges; + SetVector<ValueInfo> RefEdges, LoadRefEdges, StoreRefEdges; SetVector<GlobalValue::GUID> TypeTests; SetVector<FunctionSummary::VFuncId> TypeTestAssumeVCalls, TypeCheckedLoadVCalls; @@ -258,6 +265,7 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, // list. findRefEdges(Index, &F, RefEdges, Visited); std::vector<const Instruction *> NonVolatileLoads; + std::vector<const Instruction *> NonVolatileStores; bool HasInlineAsmMaybeReferencingInternal = false; for (const BasicBlock &BB : F) @@ -265,12 +273,34 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, if (isa<DbgInfoIntrinsic>(I)) continue; ++NumInsts; - if (isNonVolatileLoad(&I)) { - // Postpone processing of non-volatile load instructions - // See comments below - Visited.insert(&I); - NonVolatileLoads.push_back(&I); - continue; + // Regular LTO module doesn't participate in ThinLTO import, + // so no reference from it can be read/writeonly, since this + // would require importing variable as local copy + if (IsThinLTO) { + if (isNonVolatileLoad(&I)) { + // Postpone processing of non-volatile load instructions + // See comments below + Visited.insert(&I); + NonVolatileLoads.push_back(&I); + continue; + } else if (isNonVolatileStore(&I)) { + Visited.insert(&I); + NonVolatileStores.push_back(&I); + // All references from second operand of store (destination address) + // can be considered write-only if they're not referenced by any + // non-store instruction. References from first operand of store + // (stored value) can't be treated either as read- or as write-only + // so we add them to RefEdges as we do with all other instructions + // except non-volatile load. + Value *Stored = I.getOperand(0); + if (auto *GV = dyn_cast<GlobalValue>(Stored)) + // findRefEdges will try to examine GV operands, so instead + // of calling it we should add GV to RefEdges directly. + RefEdges.insert(Index.getOrInsertValueInfo(GV)); + else if (auto *U = dyn_cast<User>(Stored)) + findRefEdges(Index, U, RefEdges, Visited); + continue; + } } findRefEdges(Index, &I, RefEdges, Visited); auto CS = ImmutableCallSite(&I); @@ -361,24 +391,61 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, } } - // By now we processed all instructions in a function, except - // non-volatile loads. All new refs we add in a loop below - // are obviously constant. All constant refs are grouped in the - // end of RefEdges vector, so we can use a single integer value - // to identify them. - unsigned RefCnt = RefEdges.size(); - for (const Instruction *I : NonVolatileLoads) { - Visited.erase(I); - findRefEdges(Index, I, RefEdges, Visited); - } - std::vector<ValueInfo> Refs = RefEdges.takeVector(); - // Regular LTO module doesn't participate in ThinLTO import, - // so no reference from it can be readonly, since this would - // require importing variable as local copy - if (IsThinLTO) - for (; RefCnt < Refs.size(); ++RefCnt) + std::vector<ValueInfo> Refs; + if (IsThinLTO) { + auto AddRefEdges = [&](const std::vector<const Instruction *> &Instrs, + SetVector<ValueInfo> &Edges, + SmallPtrSet<const User *, 8> &Cache) { + for (const auto *I : Instrs) { + Cache.erase(I); + findRefEdges(Index, I, Edges, Cache); + } + }; + + // By now we processed all instructions in a function, except + // non-volatile loads and non-volatile value stores. Let's find + // ref edges for both of instruction sets + AddRefEdges(NonVolatileLoads, LoadRefEdges, Visited); + // We can add some values to the Visited set when processing load + // instructions which are also used by stores in NonVolatileStores. + // For example this can happen if we have following code: + // + // store %Derived* @foo, %Derived** bitcast (%Base** @bar to %Derived**) + // %42 = load %Derived*, %Derived** bitcast (%Base** @bar to %Derived**) + // + // After processing loads we'll add bitcast to the Visited set, and if + // we use the same set while processing stores, we'll never see store + // to @bar and @bar will be mistakenly treated as readonly. + SmallPtrSet<const llvm::User *, 8> StoreCache; + AddRefEdges(NonVolatileStores, StoreRefEdges, StoreCache); + + // If both load and store instruction reference the same variable + // we won't be able to optimize it. Add all such reference edges + // to RefEdges set. + for (auto &VI : StoreRefEdges) + if (LoadRefEdges.remove(VI)) + RefEdges.insert(VI); + + unsigned RefCnt = RefEdges.size(); + // 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. + for (auto &VI : LoadRefEdges) + RefEdges.insert(VI); + + unsigned FirstWORef = RefEdges.size(); + for (auto &VI : StoreRefEdges) + RefEdges.insert(VI); + + Refs = RefEdges.takeVector(); + for (; RefCnt < FirstWORef; ++RefCnt) Refs[RefCnt].setReadOnly(); + for (; RefCnt < Refs.size(); ++RefCnt) + Refs[RefCnt].setWriteOnly(); + } else { + Refs = RefEdges.takeVector(); + } // Explicit add hot edges to enforce importing for designated GUIDs for // sample PGO, to enable the same inlines as the profiled optimized binary. for (auto &I : F.getImportGUIDs()) @@ -526,10 +593,11 @@ static void computeVariableSummary(ModuleSummaryIndex &Index, } } - // Don't mark variables we won't be able to internalize as read-only. - GlobalVarSummary::GVarFlags VarFlags( + // Don't mark variables we won't be able to internalize as read/write-only. + bool CanBeInternalized = !V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() && - !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass()); + !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass(); + GlobalVarSummary::GVarFlags VarFlags(CanBeInternalized, CanBeInternalized); auto GVarSummary = llvm::make_unique<GlobalVarSummary>(Flags, VarFlags, RefEdges.takeVector()); if (NonRenamableLocal) @@ -647,7 +715,7 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( } else { std::unique_ptr<GlobalVarSummary> Summary = llvm::make_unique<GlobalVarSummary>( - GVFlags, GlobalVarSummary::GVarFlags(), + GVFlags, GlobalVarSummary::GVarFlags(false, false), ArrayRef<ValueInfo>{}); Index.addGlobalValueSummary(*GV, std::move(Summary)); } diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 9bfce74..085f3bd 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -7860,9 +7860,13 @@ static const auto FwdVIRef = (GlobalValueSummaryMapTy::value_type *)-8; static void resolveFwdRef(ValueInfo *Fwd, ValueInfo &Resolved) { bool ReadOnly = Fwd->isReadOnly(); + bool WriteOnly = Fwd->isWriteOnly(); + assert(!(ReadOnly && WriteOnly)); *Fwd = Resolved; if (ReadOnly) Fwd->setReadOnly(); + if (WriteOnly) + Fwd->setWriteOnly(); } /// Stores the given Name/GUID and associated summary into the Index. @@ -8092,7 +8096,8 @@ bool LLParser::ParseVariableSummary(std::string Name, GlobalValue::GUID GUID, GlobalValueSummary::GVFlags GVFlags = GlobalValueSummary::GVFlags( /*Linkage=*/GlobalValue::ExternalLinkage, /*NotEligibleToImport=*/false, /*Live=*/false, /*IsLocal=*/false, /*CanAutoHide=*/false); - GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false); + GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false, + /* WriteOnly */ false); std::vector<ValueInfo> Refs; VTableFuncList VTableFuncs; if (ParseToken(lltok::colon, "expected ':' here") || @@ -8433,10 +8438,11 @@ bool LLParser::ParseOptionalRefs(std::vector<ValueInfo> &Refs) { VContexts.push_back(VC); } while (EatIfPresent(lltok::comma)); - // Sort value contexts so that ones with readonly ValueInfo are at the end - // of VContexts vector. This is needed to match immutableRefCount() behavior. + // Sort value contexts so that ones with writeonly + // and readonly ValueInfo are at the end of VContexts vector. + // See FunctionSummary::specialRefCounts() llvm::sort(VContexts, [](const ValueContext &VC1, const ValueContext &VC2) { - return VC1.VI.isReadOnly() < VC2.VI.isReadOnly(); + return VC1.VI.getAccessSpecifier() < VC2.VI.getAccessSpecifier(); }); IdToIndexMapType IdToIndexMap; @@ -8754,24 +8760,41 @@ bool LLParser::ParseGVFlags(GlobalValueSummary::GVFlags &GVFlags) { } /// GVarFlags -/// ::= 'varFlags' ':' '(' 'readonly' ':' Flag ')' +/// ::= 'varFlags' ':' '(' 'readonly' ':' Flag +/// ',' 'writeonly' ':' Flag ')' bool LLParser::ParseGVarFlags(GlobalVarSummary::GVarFlags &GVarFlags) { assert(Lex.getKind() == lltok::kw_varFlags); Lex.Lex(); - unsigned Flag = 0; if (ParseToken(lltok::colon, "expected ':' here") || - ParseToken(lltok::lparen, "expected '(' here") || - ParseToken(lltok::kw_readonly, "expected 'readonly' here") || - ParseToken(lltok::colon, "expected ':' here")) + ParseToken(lltok::lparen, "expected '(' here")) return true; - ParseFlag(Flag); - GVarFlags.ReadOnly = Flag; + auto ParseRest = [this](unsigned int &Val) { + Lex.Lex(); + if (ParseToken(lltok::colon, "expected ':'")) + return true; + return ParseFlag(Val); + }; - if (ParseToken(lltok::rparen, "expected ')' here")) - return true; - return false; + do { + unsigned Flag = 0; + switch (Lex.getKind()) { + case lltok::kw_readonly: + if (ParseRest(Flag)) + return true; + GVarFlags.MaybeReadOnly = Flag; + break; + case lltok::kw_writeonly: + if (ParseRest(Flag)) + return true; + GVarFlags.MaybeWriteOnly = Flag; + break; + default: + return Error(Lex.getLoc(), "expected gvar flag type"); + } + } while (EatIfPresent(lltok::comma)); + return ParseToken(lltok::rparen, "expected ')' here"); } /// ModuleReference @@ -8794,7 +8817,9 @@ bool LLParser::ParseModuleReference(StringRef &ModulePath) { /// GVReference /// ::= SummaryID bool LLParser::ParseGVReference(ValueInfo &VI, unsigned &GVId) { - bool ReadOnly = EatIfPresent(lltok::kw_readonly); + bool WriteOnly = false, ReadOnly = EatIfPresent(lltok::kw_readonly); + if (!ReadOnly) + WriteOnly = EatIfPresent(lltok::kw_writeonly); if (ParseToken(lltok::SummaryID, "expected GV ID")) return true; @@ -8809,5 +8834,7 @@ bool LLParser::ParseGVReference(ValueInfo &VI, unsigned &GVId) { if (ReadOnly) VI.setReadOnly(); + if (WriteOnly) + VI.setWriteOnly(); return false; } diff --git a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp index affbe4c..a339f6e 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -984,7 +984,8 @@ static GlobalValueSummary::GVFlags getDecodedGVSummaryFlags(uint64_t RawFlags, // Decode the flags for GlobalVariable in the summary static GlobalVarSummary::GVarFlags getDecodedGVarFlags(uint64_t RawFlags) { - return GlobalVarSummary::GVarFlags((RawFlags & 0x1) ? true : false); + return GlobalVarSummary::GVarFlags((RawFlags & 0x1) ? true : false, + (RawFlags & 0x2) ? true : false); } static GlobalValue::VisibilityTypes getDecodedVisibility(unsigned Val) { @@ -5683,10 +5684,16 @@ void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableSummaryRecord( 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) +static void setSpecialRefs(std::vector<ValueInfo> &Refs, unsigned ROCnt, + unsigned WOCnt) { + // Readonly and writeonly refs are in the end of the refs list. + assert(ROCnt + WOCnt <= Refs.size()); + unsigned FirstWORef = Refs.size() - WOCnt; + unsigned RefNo = FirstWORef - ROCnt; + for (; RefNo < FirstWORef; ++RefNo) Refs[RefNo].setReadOnly(); + for (; RefNo < Refs.size(); ++RefNo) + Refs[RefNo].setWriteOnly(); } // Eagerly parse the entire summary block. This populates the GlobalValueSummary @@ -5713,9 +5720,9 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { } const uint64_t Version = Record[0]; const bool IsOldProfileFormat = Version == 1; - if (Version < 1 || Version > 6) + if (Version < 1 || Version > 7) return error("Invalid summary version " + Twine(Version) + - ". Version should be in the range [1-6]."); + ". Version should be in the range [1-7]."); Record.clear(); // Keep around the last seen summary to be used when we see an optional @@ -5814,15 +5821,19 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { unsigned InstCount = Record[2]; uint64_t RawFunFlags = 0; unsigned NumRefs = Record[3]; - unsigned NumImmutableRefs = 0; + unsigned NumRORefs = 0, NumWORefs = 0; int RefListStartIndex = 4; if (Version >= 4) { RawFunFlags = Record[3]; NumRefs = Record[4]; RefListStartIndex = 5; if (Version >= 5) { - NumImmutableRefs = Record[5]; + NumRORefs = Record[5]; RefListStartIndex = 6; + if (Version >= 7) { + NumWORefs = Record[6]; + RefListStartIndex = 7; + } } } @@ -5842,7 +5853,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { std::vector<FunctionSummary::EdgeTy> Calls = makeCallList( ArrayRef<uint64_t>(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, HasRelBF); - setImmutableRefs(Refs, NumImmutableRefs); + setSpecialRefs(Refs, NumRORefs, NumWORefs); auto FS = llvm::make_unique<FunctionSummary>( Flags, InstCount, getDecodedFFlags(RawFunFlags), /*EntryCount=*/0, std::move(Refs), std::move(Calls), std::move(PendingTypeTests), @@ -5893,7 +5904,8 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; unsigned RefArrayStart = 2; - GlobalVarSummary::GVarFlags GVF; + GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, + /* WriteOnly */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); if (Version >= 5) { GVF = getDecodedGVarFlags(Record[2]); @@ -5950,7 +5962,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { uint64_t RawFunFlags = 0; uint64_t EntryCount = 0; unsigned NumRefs = Record[4]; - unsigned NumImmutableRefs = 0; + unsigned NumRORefs = 0, NumWORefs = 0; int RefListStartIndex = 5; if (Version >= 4) { @@ -5958,13 +5970,19 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { RefListStartIndex = 6; size_t NumRefsIndex = 5; if (Version >= 5) { + unsigned NumRORefsOffset = 1; RefListStartIndex = 7; if (Version >= 6) { NumRefsIndex = 6; EntryCount = Record[5]; RefListStartIndex = 8; + if (Version >= 7) { + RefListStartIndex = 9; + NumWORefs = Record[8]; + NumRORefsOffset = 2; + } } - NumImmutableRefs = Record[RefListStartIndex - 1]; + NumRORefs = Record[RefListStartIndex - NumRORefsOffset]; } NumRefs = Record[NumRefsIndex]; } @@ -5980,7 +5998,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { ArrayRef<uint64_t>(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, false); ValueInfo VI = getValueInfoFromValueId(ValueID).first; - setImmutableRefs(Refs, NumImmutableRefs); + setSpecialRefs(Refs, NumRORefs, NumWORefs); auto FS = llvm::make_unique<FunctionSummary>( Flags, InstCount, getDecodedFFlags(RawFunFlags), EntryCount, std::move(Refs), std::move(Edges), std::move(PendingTypeTests), @@ -6027,7 +6045,8 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { uint64_t ModuleId = Record[1]; uint64_t RawFlags = Record[2]; unsigned RefArrayStart = 3; - GlobalVarSummary::GVarFlags GVF; + GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, + /* WriteOnly */ false); auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); if (Version >= 5) { GVF = getDecodedGVarFlags(Record[3]); diff --git a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp index 3812481..547889f 100644 --- a/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp +++ b/llvm/lib/Bitcode/Writer/BitcodeWriter.cpp @@ -1020,7 +1020,7 @@ static uint64_t getEncodedGVSummaryFlags(GlobalValueSummary::GVFlags Flags) { } static uint64_t getEncodedGVarFlags(GlobalVarSummary::GVarFlags Flags) { - uint64_t RawFlags = Flags.ReadOnly; + uint64_t RawFlags = Flags.MaybeReadOnly | (Flags.MaybeWriteOnly << 1); return RawFlags; } @@ -3632,11 +3632,13 @@ void ModuleBitcodeWriterBase::writePerModuleFunctionSummaryRecord( FunctionSummary *FS = cast<FunctionSummary>(Summary); writeFunctionTypeMetadataRecords(Stream, FS); + auto SpecialRefCnts = FS->specialRefCounts(); NameVals.push_back(getEncodedGVSummaryFlags(FS->flags())); NameVals.push_back(FS->instCount()); NameVals.push_back(getEncodedFFlags(FS->fflags())); NameVals.push_back(FS->refs().size()); - NameVals.push_back(FS->immutableRefCount()); + NameVals.push_back(SpecialRefCnts.first); // rorefcnt + NameVals.push_back(SpecialRefCnts.second); // worefcnt for (auto &RI : FS->refs()) NameVals.push_back(VE.getValueID(RI.getValue())); @@ -3710,7 +3712,7 @@ void ModuleBitcodeWriterBase::writeModuleLevelReferences( // 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 = 6; +static const uint64_t INDEX_VERSION = 7; /// Emit the per-module summary section alongside the rest of /// the module's bitcode. @@ -3752,7 +3754,8 @@ void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() { Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // instcount Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // fflags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // rorefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt // numrefs x valueid, n x (valueid, hotness) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3769,7 +3772,8 @@ void ModuleBitcodeWriterBase::writePerModuleGlobalValueSummary() { Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // instcount Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // fflags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // rorefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt // numrefs x valueid, n x (valueid [, rel_block_freq]) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3901,7 +3905,8 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // fflags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // entrycount Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // rorefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt // numrefs x valueid, n x (valueid) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3917,7 +3922,8 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // fflags Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // entrycount Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // numrefs - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // rorefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt // numrefs x valueid, n x (valueid, hotness) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -4019,20 +4025,24 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { // Fill in below NameVals.push_back(0); // numrefs - NameVals.push_back(0); // immutablerefcnt + NameVals.push_back(0); // rorefcnt + NameVals.push_back(0); // worefcnt - unsigned Count = 0, ImmutableRefCnt = 0; + unsigned Count = 0, RORefCnt = 0, WORefCnt = 0; for (auto &RI : FS->refs()) { auto RefValueId = getValueId(RI.getGUID()); if (!RefValueId) continue; NameVals.push_back(*RefValueId); if (RI.isReadOnly()) - ImmutableRefCnt++; + RORefCnt++; + else if (RI.isWriteOnly()) + WORefCnt++; Count++; } NameVals[6] = Count; - NameVals[7] = ImmutableRefCnt; + NameVals[7] = RORefCnt; + NameVals[8] = WORefCnt; bool HasProfileData = false; for (auto &EI : FS->calls()) { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index 51b1a03..eb5760d 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -2891,7 +2891,8 @@ void AssemblyWriter::printAliasSummary(const AliasSummary *AS) { } void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) { - Out << ", varFlags: (readonly: " << GS->VarFlags.ReadOnly << ")"; + Out << ", varFlags: (readonly: " << GS->VarFlags.MaybeReadOnly << ", " + << "writeonly: " << GS->VarFlags.MaybeWriteOnly << ")"; auto VTableFuncs = GS->vTableFuncs(); if (!VTableFuncs.empty()) { @@ -3101,6 +3102,8 @@ void AssemblyWriter::printSummary(const GlobalValueSummary &Summary) { Out << FS; if (Ref.isReadOnly()) Out << "readonly "; + else if (Ref.isWriteOnly()) + Out << "writeonly "; Out << "^" << Machine.getGUIDSlot(Ref.getGUID()); } Out << ")"; diff --git a/llvm/lib/IR/ModuleSummaryIndex.cpp b/llvm/lib/IR/ModuleSummaryIndex.cpp index 18b7ac09..9f347d8 100644 --- a/llvm/lib/IR/ModuleSummaryIndex.cpp +++ b/llvm/lib/IR/ModuleSummaryIndex.cpp @@ -23,6 +23,8 @@ using namespace llvm; STATISTIC(ReadOnlyLiveGVars, "Number of live global variables marked read only"); +STATISTIC(WriteOnlyLiveGVars, + "Number of live global variables marked write only"); FunctionSummary FunctionSummary::ExternalNode = FunctionSummary::makeDummyFunctionSummary({}); @@ -45,15 +47,18 @@ bool ValueInfo::canAutoHide() const { }); } -// Gets the number of immutable refs in RefEdgeList -unsigned FunctionSummary::immutableRefCount() const { - // Here we take advantage of having all readonly references +// Gets the number of readonly and writeonly refs in RefEdgeList +std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const { + // Here we take advantage of having all readonly and writeonly references // located in the end of the RefEdgeList. auto Refs = refs(); - unsigned ImmutableRefCnt = 0; - for (int I = Refs.size() - 1; I >= 0 && Refs[I].isReadOnly(); --I) - ImmutableRefCnt++; - return ImmutableRefCnt; + unsigned RORefCnt = 0, WORefCnt = 0; + int I; + for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I) + WORefCnt++; + for (; I >= 0 && Refs[I].isReadOnly(); --I) + RORefCnt++; + return {RORefCnt, WORefCnt}; } // Collect for the given module the list of function it defines @@ -99,48 +104,56 @@ bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const { return false; } -static void propagateConstantsToRefs(GlobalValueSummary *S) { - // If reference is not readonly then referenced summary is not - // readonly either. Note that: +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. Tracking them properly requires more complex analysis - // then we have now. + // not readonly or writeonly. Tracking them properly requires more complex + // analysis then we have now. // // - AliasSummary objects have no refs at all so this function is a no-op // for them. for (auto &VI : S->refs()) { - if (VI.isReadOnly()) { - // We only mark refs as readonly when computing function summaries on - // analysis phase. - assert(isa<FunctionSummary>(S)); - continue; - } + assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S)); for (auto &Ref : VI.getSummaryList()) - // If references to alias is not readonly then aliasee is not readonly - if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) - GVS->setReadOnly(false); + // If references to alias is not read/writeonly then aliasee + // is not read/writeonly + if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) { + if (!VI.isReadOnly()) + GVS->setReadOnly(false); + if (!VI.isWriteOnly()) + GVS->setWriteOnly(false); + } } } -// Do the constant propagation in combined index. -// The goal of constant propagation is internalization of readonly -// variables. To determine which variables are readonly and which -// are not we take following steps: -// - During analysis we speculatively assign readonly attribute to -// all variables which can be internalized. When computing function -// summary we also assign readonly attribute to a reference if -// function doesn't modify referenced variable. +// Do the access attribute propagation in combined index. +// The goal of attribute propagation is internalization of readonly (RO) +// or writeonly (WO) variables. To determine which variables are RO or WO +// and which are not we take following steps: +// - During analysis we speculatively assign readonly and writeonly +// attribute to all variables which can be internalized. When computing +// function summary we also assign readonly or writeonly attribute to a +// reference if function doesn't modify referenced variable (readonly) +// or doesn't read it (writeonly). +// +// - After computing dead symbols in combined index we do the attribute +// propagation. During this step we: +// a. clear RO and WO attributes from variables which are preserved or +// can't be imported +// b. clear RO and WO attributes from variables referenced by any global +// variable initializer +// c. clear RO attribute from variable referenced by a function when +// reference is not readonly +// d. clear WO attribute from variable referenced by a function when +// reference is not writeonly // -// - After computing dead symbols in combined index we do the constant -// propagation. During this step we clear readonly attribute from -// all variables which: -// a. are preserved or can't be imported -// b. referenced by any global variable initializer -// c. referenced by a function and reference is not readonly +// Because of (c, d) we don't internalize variables read by function A +// and modified by function B. // // Internalization itself happens in the backend after import is finished -// See internalizeImmutableGVs. -void ModuleSummaryIndex::propagateConstants( +// See internalizeGVsAfterImport. +void ModuleSummaryIndex::propagateAttributes( const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { for (auto &P : *this) for (auto &S : P.second.SummaryList) { @@ -148,29 +161,36 @@ void ModuleSummaryIndex::propagateConstants( // We don't examine references from dead objects continue; - // Global variable can't be marked read only if it is not eligible - // to import since we need to ensure that all external references - // get a local (imported) copy. It also can't be marked read only - // if it or any alias (since alias points to the same memory) are - // preserved or notEligibleToImport, since either of those means - // there could be writes that are not visible (because preserved - // means it could have external to DSO writes, and notEligibleToImport - // means it could have writes via inline assembly leading it to be - // in the @llvm.*used). + // Global variable can't be marked read/writeonly if it is not eligible + // to import since we need to ensure that all external references get + // a local (imported) copy. It also can't be marked read/writeonly if + // it or any alias (since alias points to the same memory) are preserved + // or notEligibleToImport, since either of those means there could be + // writes (or reads in case of writeonly) that are not visible (because + // preserved means it could have external to DSO writes or reads, and + // notEligibleToImport means it could have writes or reads via inline + // assembly leading it to be in the @llvm.*used). if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject())) // Here we intentionally pass S.get() not GVS, because S could be // an alias. - if (!canImportGlobalVar(S.get()) || GUIDPreservedSymbols.count(P.first)) + if (!canImportGlobalVar(S.get()) || + GUIDPreservedSymbols.count(P.first)) { GVS->setReadOnly(false); - propagateConstantsToRefs(S.get()); + GVS->setWriteOnly(false); + } + propagateAttributesToRefs(S.get()); } if (llvm::AreStatisticsEnabled()) for (auto &P : *this) if (P.second.SummaryList.size()) if (auto *GVS = dyn_cast<GlobalVarSummary>( P.second.SummaryList[0]->getBaseObject())) - if (isGlobalValueLive(GVS) && GVS->isReadOnly()) - ReadOnlyLiveGVars++; + if (isGlobalValueLive(GVS)) { + if (GVS->maybeReadOnly()) + ReadOnlyLiveGVars++; + if (GVS->maybeWriteOnly()) + WriteOnlyLiveGVars++; + } } // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot) @@ -333,7 +353,13 @@ static void defineExternalNode(raw_ostream &OS, const char *Pfx, static bool hasReadOnlyFlag(const GlobalValueSummary *S) { if (auto *GVS = dyn_cast<GlobalVarSummary>(S)) - return GVS->isReadOnly(); + return GVS->maybeReadOnly(); + return false; +} + +static bool hasWriteOnlyFlag(const GlobalValueSummary *S) { + if (auto *GVS = dyn_cast<GlobalVarSummary>(S)) + return GVS->maybeWriteOnly(); return false; } @@ -358,12 +384,14 @@ void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { // 0 - alias // 1 - reference // 2 - constant reference - // Other value: (hotness - 3). - TypeOrHotness += 3; + // 3 - writeonly reference + // Other value: (hotness - 4). + TypeOrHotness += 4; static const char *EdgeAttrs[] = { " [style=dotted]; // alias", " [style=dashed]; // ref", " [style=dashed,color=forestgreen]; // const-ref", + " [style=dashed,color=violetred]; // writeOnly-ref", " // call (hotness : Unknown)", " [color=blue]; // call (hotness : Cold)", " // call (hotness : None)", @@ -408,6 +436,8 @@ void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { A.add("shape", "Mrecord", "variable"); if (Flags.Live && hasReadOnlyFlag(SummaryIt.second)) A.addComment("immutable"); + if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second)) + A.addComment("writeOnly"); } if (Flags.DSOLocal) A.addComment("dsoLocal"); @@ -429,10 +459,11 @@ void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { for (auto &SummaryIt : GVSMap) { auto *GVS = SummaryIt.second; for (auto &R : GVS->refs()) - Draw(SummaryIt.first, R.getGUID(), R.isReadOnly() ? -1 : -2); + Draw(SummaryIt.first, R.getGUID(), + R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3)); if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) { - Draw(SummaryIt.first, AS->getAliaseeGUID(), -3); + Draw(SummaryIt.first, AS->getAliaseeGUID(), -4); continue; } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 4ed1370..6450689 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -192,8 +192,10 @@ void llvm::computeLTOCacheKey( AddUnsigned(VI.isDSOLocal()); AddUsedCfiGlobal(VI.getGUID()); } - if (auto *GVS = dyn_cast<GlobalVarSummary>(GS)) - AddUnsigned(GVS->isReadOnly()); + if (auto *GVS = dyn_cast<GlobalVarSummary>(GS)) { + AddUnsigned(GVS->maybeReadOnly()); + AddUnsigned(GVS->maybeWriteOnly()); + } if (auto *FS = dyn_cast<FunctionSummary>(GS)) { for (auto &TT : FS->type_tests()) UsedTypeIds.insert(TT); @@ -371,9 +373,9 @@ void llvm::thinLTOResolvePrevailingInIndex( GUIDPreservedSymbols); } -static bool isWeakWriteableObject(GlobalValueSummary *GVS) { +static bool isWeakObjectWithRWAccess(GlobalValueSummary *GVS) { if (auto *VarSummary = dyn_cast<GlobalVarSummary>(GVS->getBaseObject())) - return !VarSummary->isReadOnly() && + return !VarSummary->maybeReadOnly() && !VarSummary->maybeWriteOnly() && (VarSummary->linkage() == GlobalValue::WeakODRLinkage || VarSummary->linkage() == GlobalValue::LinkOnceODRLinkage); return false; @@ -394,11 +396,12 @@ static void thinLTOInternalizeAndPromoteGUID( // We can't internalize available_externally globals because this // can break function pointer equality. S->linkage() != GlobalValue::AvailableExternallyLinkage && - // Functions and read-only variables with linkonce_odr and weak_odr - // linkage can be internalized. We can't internalize linkonce_odr - // and weak_odr variables which are modified somewhere in the - // program because reads and writes will become inconsistent. - !isWeakWriteableObject(S.get())) + // Functions and read-only variables with linkonce_odr and + // weak_odr linkage can be internalized. We can't internalize + // linkonce_odr and weak_odr variables which are both modified + // and read somewhere in the program because reads and writes + // will become inconsistent. + !isWeakObjectWithRWAccess(S.get())) S->setLinkage(GlobalValue::InternalLinkage); } } diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp b/llvm/lib/Transforms/IPO/FunctionImport.cpp index 9207f5f..62c7fbd 100644 --- a/llvm/lib/Transforms/IPO/FunctionImport.cpp +++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -850,14 +850,16 @@ void llvm::computeDeadSymbolsWithConstProp( bool ImportEnabled) { computeDeadSymbols(Index, GUIDPreservedSymbols, isPrevailing); if (ImportEnabled) { - Index.propagateConstants(GUIDPreservedSymbols); + Index.propagateAttributes(GUIDPreservedSymbols); } else { - // If import is disabled we should drop read-only attribute + // If import is disabled we should drop read/write-only attribute // from all summaries to prevent internalization. for (auto &P : Index) for (auto &S : P.second.SummaryList) - if (auto *GVS = dyn_cast<GlobalVarSummary>(S.get())) + if (auto *GVS = dyn_cast<GlobalVarSummary>(S.get())) { GVS->setReadOnly(false); + GVS->setWriteOnly(false); + } } } @@ -1064,7 +1066,7 @@ static Function *replaceAliasWithAliasee(Module *SrcModule, GlobalAlias *GA) { // Internalize values that we marked with specific attribute // in processGlobalForThinLTO. -static void internalizeImmutableGVs(Module &M) { +static void internalizeGVsAfterImport(Module &M) { for (auto &GV : M.globals()) // Skip GVs which have been converted to declarations // by dropDeadSymbols. @@ -1197,7 +1199,7 @@ Expected<bool> FunctionImporter::importFunctions( NumImportedModules++; } - internalizeImmutableGVs(DestModule); + internalizeGVsAfterImport(DestModule); NumImportedFunctions += (ImportedCount - ImportedGVCount); NumImportedGlobalVars += ImportedGVCount; diff --git a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp index 8e80ee2..c9cc099 100644 --- a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp +++ b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp @@ -229,11 +229,11 @@ void FunctionImportGlobalProcessing::processGlobalForThinLTO(GlobalValue &GV) { } } - // Mark read-only variables which can be imported with specific attribute. - // We can't internalize them now because IRMover will fail to link variable - // definitions to their external declarations during ThinLTO import. We'll - // internalize read-only variables later, after import is finished. - // See internalizeImmutableGVs. + // Mark read/write-only variables which can be imported with specific + // attribute. We can't internalize them now because IRMover will fail + // to link variable definitions to their external declarations during + // ThinLTO import. We'll internalize read-only variables later, after + // import is finished. See internalizeGVsAfterImport. // // If global value dead stripping is not enabled in summary then // propagateConstants hasn't been run. We can't internalize GV @@ -241,7 +241,8 @@ void FunctionImportGlobalProcessing::processGlobalForThinLTO(GlobalValue &GV) { if (!GV.isDeclaration() && VI && ImportIndex.withGlobalValueDeadStripping()) { const auto &SL = VI.getSummaryList(); auto *GVS = SL.empty() ? nullptr : dyn_cast<GlobalVarSummary>(SL[0].get()); - if (GVS && GVS->isReadOnly()) + // At this stage "maybe" is "definitely" + if (GVS && (GVS->maybeReadOnly() || GVS->maybeWriteOnly())) cast<GlobalVariable>(&GV)->addAttribute("thinlto-internalize"); } diff --git a/llvm/test/Assembler/thinlto-summary.ll b/llvm/test/Assembler/thinlto-summary.ll index 47444c6..23d83e3 100644 --- a/llvm/test/Assembler/thinlto-summary.ll +++ b/llvm/test/Assembler/thinlto-summary.ll @@ -9,7 +9,7 @@ ; Check a function that makes several calls with various profile hotness, and a ; reference (also tests forward references to function and variables in calls ; and refs). -^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (readonly ^13, ^14)))) +^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (writeonly ^14, readonly ^13, ^11)))) ; Function with a call that has relative block frequency instead of profile ; hotness. @@ -33,7 +33,7 @@ ^13 = gv: (guid: 12, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0), varFlags: (readonly: 1)))) ; Test a dsoLocal variable. -^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), varFlags: (readonly: 0)))) +^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), varFlags: (writeonly: 0)))) ; Functions with various flag combinations (notEligibleToImport, Live, ; combinations of optional function flags). @@ -67,7 +67,7 @@ ; Make sure we get back from llvm-dis essentially what we put in via llvm-as. ; CHECK: ^0 = module: (path: "thinlto-summary1.o", hash: (1369602428, 2747878711, 259090915, 2507395659, 1141468049)) ; CHECK: ^1 = module: (path: "thinlto-summary2.o", hash: (2998369023, 4283347029, 1195487472, 2757298015, 1852134156)) -; CHECK: ^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (^14, readonly ^13)))) +; CHECK: ^2 = gv: (guid: 1, summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 10, calls: ((callee: ^15, hotness: hot), (callee: ^17, hotness: cold), (callee: ^16, hotness: none)), refs: (^11, readonly ^13, writeonly ^14)))) ; CHECK: ^3 = gv: (guid: 2, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 10, calls: ((callee: ^15))))) ; CHECK: ^4 = gv: (guid: 3, summaries: (function: (module: ^0, flags: (linkage: internal, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 1))) ; CHECK: ^5 = gv: (guid: 4, summaries: (alias: (module: ^0, flags: (linkage: private, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), aliasee: ^14))) @@ -76,10 +76,10 @@ ; 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)))) -; CHECK: ^12 = gv: (guid: 11, summaries: (variable: (module: ^0, flags: (linkage: appending, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 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)))) -; CHECK: ^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 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)))) +; 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)))) ; CHECK: ^15 = gv: (guid: 14, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 1, live: 1, dsoLocal: 0, canAutoHide: 0), insts: 1))) ; CHECK: ^16 = gv: (guid: 15, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 1, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 0)))) ; CHECK: ^17 = gv: (guid: 16, summaries: (function: (module: ^1, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 1, funcFlags: (readNone: 0, readOnly: 1, noRecurse: 0, returnDoesNotAlias: 1, noInline: 0), calls: ((callee: ^15))))) diff --git a/llvm/test/Assembler/thinlto-vtable-summary.ll b/llvm/test/Assembler/thinlto-vtable-summary.ll index 96c021d..ac4c703 100644 --- a/llvm/test/Assembler/thinlto-vtable-summary.ll +++ b/llvm/test/Assembler/thinlto-vtable-summary.ll @@ -29,9 +29,9 @@ declare i32 @_ZN1C1fEi(%struct.C*, i32) ^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 +^2 = gv: (name: "_ZTV1B", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 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 +^4 = gv: (name: "_ZTV1C", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 0, writeonly: 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 diff --git a/llvm/test/Bitcode/summary_version.ll b/llvm/test/Bitcode/summary_version.ll index fc3b3bd..e531a07 100644 --- a/llvm/test/Bitcode/summary_version.ll +++ b/llvm/test/Bitcode/summary_version.ll @@ -2,7 +2,7 @@ ; RUN: opt -module-summary %s -o - | llvm-bcanalyzer -dump | FileCheck %s ; CHECK: <GLOBALVAL_SUMMARY_BLOCK -; CHECK: <VERSION op0=6/> +; CHECK: <VERSION op0=7/> diff --git a/llvm/test/Bitcode/thinlto-alias.ll b/llvm/test/Bitcode/thinlto-alias.ll index ac82664..a1412f5 100644 --- a/llvm/test/Bitcode/thinlto-alias.ll +++ b/llvm/test/Bitcode/thinlto-alias.ll @@ -21,7 +21,7 @@ ; CHECK-NEXT: <FLAGS ; See if the call to func is registered. ; The value id 1 matches the second FUNCTION record above. -; CHECK-NEXT: <PERMODULE {{.*}} op6=1/> +; CHECK-NEXT: <PERMODULE {{.*}} op7=1/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK @@ -34,7 +34,7 @@ ; COMBINED-NEXT: <VALUE_GUID op0=[[ALIASID:[0-9]+]] op1=-5751648690987223394/> ; COMBINED-NEXT: <VALUE_GUID ; COMBINED-NEXT: <VALUE_GUID op0=[[ALIASEEID:[0-9]+]] op1=-1039159065113703048/> -; COMBINED-NEXT: <COMBINED {{.*}} op8=[[ALIASID]]/> +; COMBINED-NEXT: <COMBINED {{.*}} op9=[[ALIASID]]/> ; COMBINED-NEXT: <COMBINED {{.*}} ; COMBINED-NEXT: <COMBINED_ALIAS {{.*}} op3=[[ALIASEEID]] ; COMBINED-NEXT: </GLOBALVAL_SUMMARY_BLOCK diff --git a/llvm/test/Bitcode/thinlto-alias2.ll b/llvm/test/Bitcode/thinlto-alias2.ll index 8b04ee7..d438706 100644 --- a/llvm/test/Bitcode/thinlto-alias2.ll +++ b/llvm/test/Bitcode/thinlto-alias2.ll @@ -5,7 +5,7 @@ ; CHECK: <GLOBALVAL_SUMMARY_BLOCK ; CHECK-NEXT: <VERSION ; CHECK-NEXT: <FLAGS -; CHECK-NEXT: <PERMODULE {{.*}} op4=0 op5=0 op6=[[ALIASID:[0-9]+]]/> +; CHECK-NEXT: <PERMODULE {{.*}} op4=0 op5=0 op6=0 op7=[[ALIASID:[0-9]+]]/> ; CHECK-NEXT: <PERMODULE {{.*}} op0=[[ALIASEEID:[0-9]+]] ; CHECK-NEXT: <ALIAS {{.*}} op0=[[ALIASID]] {{.*}} op2=[[ALIASEEID]]/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll index d4b4d549..e6fc939 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll @@ -7,9 +7,9 @@ ; CHECK-NEXT: <VERSION ; CHECK-NEXT: <FLAGS ; "op7" is a call to "callee" function. -; CHECK-NEXT: <PERMODULE {{.*}} op8=3 op9=[[ALIASID:[0-9]+]]/> +; CHECK-NEXT: <PERMODULE {{.*}} op9=3 op10=[[ALIASID:[0-9]+]]/> ; "another_caller" has only references but no calls. -; CHECK-NEXT: <PERMODULE {{.*}} op4=3 {{.*}} op8={{[0-9]+}}/> +; CHECK-NEXT: <PERMODULE {{.*}} op4=3 {{.*}} op9={{[0-9]+}}/> ; CHECK-NEXT: <PERMODULE {{.*}} op0=[[ALIASEEID:[0-9]+]] ; CHECK-NEXT: <ALIAS {{.*}} op0=[[ALIASID]] {{.*}} op2=[[ALIASEEID]]/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll index b9613f7..2bbab0c 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll @@ -18,7 +18,7 @@ ; CHECK-NEXT: <VERSION ; CHECK-NEXT: <FLAGS ; See if the call to func is registered, using the expected hotness type. -; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op6=1 op7=2/> +; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op7=1 op8=2/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK ; CHECK-NEXT: blob data = 'mainfunc{{.*}}' @@ -31,7 +31,7 @@ ; COMBINED-NEXT: <COMBINED ; See if the call to func is registered, using the expected hotness type. ; op6=2 which is hotnessType::None. -; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op8=[[FUNCID]] op9=2/> +; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op9=[[FUNCID]] op10=2/> ; COMBINED-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; ModuleID = 'thinlto-function-summary-callgraph.ll' diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll index e19a4c3..d444ee7 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph-profile-summary.ll @@ -49,7 +49,7 @@ ; CHECK-NEXT: <FLAGS ; CHECK-NEXT: <VALUE_GUID op0=25 op1=123/> ; op4=hot1 op6=cold op8=hot2 op10=hot4 op12=none1 op14=hot3 op16=none2 op18=none3 op20=123 -; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op6=1 op7=3 op8=5 op9=1 op10=2 op11=3 op12=4 op13=1 op14=6 op15=2 op16=3 op17=3 op18=7 op19=2 op20=8 op21=2 op22=25 op23=4/> +; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op7=1 op8=3 op9=5 op10=1 op11=2 op12=3 op13=4 op14=1 op15=6 op16=2 op17=3 op18=3 op19=7 op20=2 op21=8 op22=2 op23=25 op24=4/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK @@ -72,7 +72,7 @@ ; COMBINED-NEXT: <COMBINED abbrevid= ; COMBINED-NEXT: <COMBINED abbrevid= ; COMBINED-NEXT: <COMBINED abbrevid= -; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op8=[[HOT1:.*]] op9=3 op10=[[COLD:.*]] op11=1 op12=[[HOT2:.*]] op13=3 op14=[[NONE1:.*]] op15=2 op16=[[HOT3:.*]] op17=3 op18=[[NONE2:.*]] op19=2 op20=[[NONE3:.*]] op21=2/> +; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op9=[[HOT1:.*]] op10=3 op11=[[COLD:.*]] op12=1 op13=[[HOT2:.*]] op14=3 op15=[[NONE1:.*]] op16=2 op17=[[HOT3:.*]] op18=3 op19=[[NONE2:.*]] op20=2 op21=[[NONE3:.*]] op22=2/> ; COMBINED_NEXT: <COMBINED abbrevid= ; COMBINED_NEXT: </GLOBALVAL_SUMMARY_BLOCK> diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll index ae0d9beb..7d13ae4 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll @@ -14,7 +14,7 @@ ; CHECK-NEXT: <VERSION ; CHECK-NEXT: <FLAGS ; See if the call to func is registered. -; CHECK-NEXT: <PERMODULE_RELBF {{.*}} op4=1 {{.*}} op8=256 +; CHECK-NEXT: <PERMODULE_RELBF {{.*}} op4=1 {{.*}} op9=256 ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK ; CHECK-NEXT: blob data = 'undefinedglobmainfunc{{.*}}' diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll index 8bf65ab..1e23b10 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph-sample-profile-summary.ll @@ -32,7 +32,7 @@ ; CHECK-NEXT: <FLAGS ; CHECK-NEXT: <VALUE_GUID op0=26 op1=123/> ; op4=none1 op6=hot1 op8=cold1 op10=none2 op12=hot2 op14=cold2 op16=none3 op18=hot3 op20=cold3 op22=123 -; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op6=7 op7=0 op8=1 op9=3 op10=4 op11=1 op12=8 op13=0 op14=2 op15=3 op16=5 op17=1 op18=9 op19=0 op20=3 op21=3 op22=6 op23=1 op24=26 op25=4/> +; CHECK-NEXT: <PERMODULE_PROFILE {{.*}} op7=7 op8=0 op9=1 op10=3 op11=4 op12=1 op13=8 op14=0 op15=2 op16=3 op17=5 op18=1 op19=9 op20=0 op21=3 op22=3 op23=6 op24=1 op25=26 op26=4/> ; CHECK-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK @@ -59,7 +59,7 @@ ; COMBINED-NEXT: <COMBINED abbrevid= ; COMBINED-NEXT: <COMBINED abbrevid= ; COMBINED-NEXT: <COMBINED abbrevid= -; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op8=[[NONE1:.*]] op9=0 op10=[[HOT1:.*]] op11=3 op12=[[COLD1:.*]] op13=1 op14=[[NONE2:.*]] op15=0 op16=[[HOT2:.*]] op17=3 op18=[[COLD2:.*]] op19=1 op20=[[NONE3:.*]] op21=0 op22=[[HOT3:.*]] op23=3 op24=[[COLD3:.*]] op25=1/> +; COMBINED-NEXT: <COMBINED_PROFILE {{.*}} op9=[[NONE1:.*]] op10=0 op11=[[HOT1:.*]] op12=3 op13=[[COLD1:.*]] op14=1 op15=[[NONE2:.*]] op16=0 op17=[[HOT2:.*]] op18=3 op19=[[COLD2:.*]] op20=1 op21=[[NONE3:.*]] op22=0 op23=[[HOT3:.*]] op24=3 op25=[[COLD3:.*]] op26=1/> ; COMBINED_NEXT: <COMBINED abbrevid= ; COMBINED_NEXT: </GLOBALVAL_SUMMARY_BLOCK> diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll index 0969b84..765efcf 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll @@ -34,7 +34,7 @@ ; COMBINED-NEXT: <VALUE_GUID ; COMBINED-NEXT: <COMBINED ; See if the call to func is registered. -; COMBINED-NEXT: <COMBINED {{.*}} op8=[[FUNCID]]/> +; COMBINED-NEXT: <COMBINED {{.*}} op9=[[FUNCID]]/> ; COMBINED-NEXT: </GLOBALVAL_SUMMARY_BLOCK> ; ModuleID = 'thinlto-function-summary-callgraph.ll' diff --git a/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll b/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll index 8211df9..5a33daa 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll @@ -41,27 +41,27 @@ ; CHECK: <GLOBALVAL_SUMMARY_BLOCK ; Function main contains call to func, as well as address reference to func: ; op0=main op4=func op5=func -; CHECK-DAG: <PERMODULE {{.*}} op0=11 op1=0 {{.*}} op4=1 op5=0 op6=2 op7=2/> +; CHECK-DAG: <PERMODULE {{.*}} op0=11 op1=0 {{.*}} op4=1 op5=0 op6=0 op7=2 op8=2/> ; Function W contains a call to func3 as well as a reference to globalvar: ; op0=W op4=globalvar op5=func3 -; CHECK-DAG: <PERMODULE {{.*}} op0=6 op1=5 {{.*}} op4=1 op5=0 op6=1 op7=5/> +; CHECK-DAG: <PERMODULE {{.*}} op0=6 op1=5 {{.*}} op4=1 op5=0 op6=0 op7=1 op8=5/> ; Function X contains call to foo, as well as address reference to foo ; which is in the same instruction as the call: ; op0=X op4=foo op5=foo -; CHECK-DAG: <PERMODULE {{.*}} op0=7 op1=1 {{.*}} op4=1 op5=0 op6=4 op7=4/> +; CHECK-DAG: <PERMODULE {{.*}} op0=7 op1=1 {{.*}} op4=1 op5=0 op6=0 op7=4 op8=4/> ; Function Y contains call to func2, and ensures we don't incorrectly add ; a reference to it when reached while earlier analyzing the phi using its ; return value: ; op0=Y op4=func2 -; CHECK-DAG: <PERMODULE {{.*}} op0=8 op1=72 {{.*}} op4=0 op5=0 op6=3/> +; CHECK-DAG: <PERMODULE {{.*}} op0=8 op1=72 {{.*}} op4=0 op5=0 op6=0 op7=3/> ; Function Z contains call to func2, and ensures we don't incorrectly add ; a reference to it when reached while analyzing subsequent use of its return ; value: ; op0=Z op4=func2 -; CHECK-DAG: <PERMODULE {{.*}} op0=9 op1=3 {{.*}} op4=0 op5=0 op6=3/> +; CHECK-DAG: <PERMODULE {{.*}} op0=9 op1=3 {{.*}} op4=0 op5=0 op6=0 op7=3/> ; Variable bar initialization contains address reference to func: ; op0=bar op2=func -; CHECK-DAG: <PERMODULE_GLOBALVAR_INIT_REFS {{.*}} op0=0 op1=0 op2=1 op3=2/> +; CHECK-DAG: <PERMODULE_GLOBALVAR_INIT_REFS {{.*}} op0=0 op1=0 op2=3 op3=2/> ; CHECK: </GLOBALVAL_SUMMARY_BLOCK> ; CHECK: <STRTAB_BLOCK @@ -154,11 +154,11 @@ entry: ; DIS-DAG: = gv: (name: "foo") ; guid = 6699318081062747564 ; DIS-DAG: = gv: (name: "func") ; guid = 7289175272376759421 ; DIS-DAG: = gv: (name: "func3") ; guid = 11517462787082255043 -; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1)))) ; guid = 12887606300320728018 +; DIS-DAG: = gv: (name: "globalvar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1)))) ; guid = 12887606300320728018 ; DIS-DAG: = gv: (name: "func2") ; guid = 14069196320850861797 ; DIS-DAG: = gv: (name: "llvm.ctpop.i8") ; guid = 15254915475081819833 ; DIS-DAG: = gv: (name: "main", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), insts: 9, calls: ((callee: ^{{.*}})), refs: (^{{.*}})))) ; guid = 15822663052811949562 -; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1), refs: (^{{.*}})))) ; guid = 16434608426314478903 +; DIS-DAG: = gv: (name: "bar", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 0, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1), refs: (^{{.*}})))) ; guid = 16434608426314478903 ; Don't try to match the exact GUID. Since it is private, the file path ; will get hashed, and that will be test dependent. ; DIS-DAG: = gv: (name: "Y", summaries: (function: (module: ^0, flags: (linkage: private, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 14, calls: ((callee: ^{{.*}}))))) ; guid = diff --git a/llvm/test/ThinLTO/X86/Inputs/dot-dumper2.ll b/llvm/test/ThinLTO/X86/Inputs/dot-dumper2.ll new file mode 100644 index 0000000..6d9783e --- /dev/null +++ b/llvm/test/ThinLTO/X86/Inputs/dot-dumper2.ll @@ -0,0 +1,4 @@ +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@A = local_unnamed_addr global i32 10, align 4 diff --git a/llvm/test/ThinLTO/X86/dot-dumper2.ll b/llvm/test/ThinLTO/X86/dot-dumper2.ll new file mode 100644 index 0000000..8cc9822 --- /dev/null +++ b/llvm/test/ThinLTO/X86/dot-dumper2.ll @@ -0,0 +1,43 @@ +; Test writeOnly attribute +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/dot-dumper2.ll -o %t2.bc +; RUN: llvm-lto2 run -save-temps %t1.bc %t2.bc -o %t3 \ +; RUN: -r=%t1.bc,main,px \ +; RUN: -r=%t1.bc,A, \ +; RUN: -r=%t2.bc,A,p + +; RUN: cat %t3.index.dot | FileCheck --check-prefix=COMBINED %s + +; COMBINED: digraph Summary { +; COMBINED-NEXT: // Module: +; COMBINED-NEXT: subgraph cluster_0 { +; COMBINED-NEXT: style = filled; +; COMBINED-NEXT: color = lightgrey; +; COMBINED-NEXT: label = +; COMBINED-NEXT: node [style=filled,fillcolor=lightblue]; +; COMBINED-NEXT: M0_[[MAIN:[0-9]+]] [shape="record",label="main|extern (inst: 2, ffl: 00000)}"]; // function +; COMBINED-NEXT: // Edges: +; COMBINED-NEXT: } +; COMBINED-NEXT: // Module: +; COMBINED-NEXT: subgraph cluster_1 { +; COMBINED-NEXT: style = filled; +; COMBINED-NEXT: color = lightgrey; +; COMBINED-NEXT: label = +; COMBINED-NEXT: node [style=filled,fillcolor=lightblue]; +; COMBINED-NEXT: M1_[[A:[0-9]+]] [shape="Mrecord",label="A|extern}"]; // variable, writeOnly +; COMBINED-NEXT: // Edges: +; COMBINED-NEXT: } +; COMBINED-NEXT: // Cross-module edges: +; COMBINED-NEXT: M0_[[MAIN]] -> M1_[[A]] [style=dashed,color=violetred]; // writeOnly-ref +; COMBINED-NEXT: } + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +@A = external local_unnamed_addr global i32, align 4 + +; Function Attrs: nounwind uwtable +define i32 @main() local_unnamed_addr { + store i32 42, i32* @A, align 4 + ret i32 0 +} diff --git a/llvm/test/ThinLTO/X86/index-const-prop2.ll b/llvm/test/ThinLTO/X86/index-const-prop2.ll index 96b7593..a5fbbdf 100644 --- a/llvm/test/ThinLTO/X86/index-const-prop2.ll +++ b/llvm/test/ThinLTO/X86/index-const-prop2.ll @@ -11,8 +11,10 @@ ; RUN: -r=%t2.bc,rand, \ ; RUN: -r=%t2.bc,gBar,pl \ ; RUN: -r=%t1.bc,main,plx \ +; RUN: -r=%t1.bc,main2,pl \ ; RUN: -r=%t1.bc,foo, \ ; RUN: -r=%t1.bc,bar, \ +; RUN: -r=%t1.bc,baz, \ ; RUN: -r=%t1.bc,gBar, \ ; RUN: -o %t3 ; RUN: llvm-dis %t3.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT @@ -26,11 +28,32 @@ ; RUN: -r=%t2.bc,rand, \ ; RUN: -r=%t2.bc,gBar,plx \ ; RUN: -r=%t1.bc,main,plx \ +; RUN: -r=%t1.bc,main2,pl \ ; RUN: -r=%t1.bc,foo, \ ; RUN: -r=%t1.bc,bar, \ +; RUN: -r=%t1.bc,baz, \ ; RUN: -r=%t1.bc,gBar, \ -; RUN: -o %t3 -; RUN: llvm-dis %t3.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT2 +; RUN: -o %t4 +; RUN: llvm-dis %t4.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT2 + +; RUN: llvm-lto2 run %t1.bc %t2.bc -save-temps \ +; RUN: -r=%t2.bc,foo,pl \ +; RUN: -r=%t2.bc,bar,pl \ +; RUN: -r=%t2.bc,baz,pl \ +; RUN: -r=%t2.bc,rand, \ +; RUN: -r=%t2.bc,gBar,pl \ +; RUN: -r=%t1.bc,main,pl \ +; RUN: -r=%t1.bc,main2,plx \ +; RUN: -r=%t1.bc,foo, \ +; RUN: -r=%t1.bc,bar, \ +; RUN: -r=%t1.bc,baz, \ +; RUN: -r=%t1.bc,gBar, \ +; RUN: -o %t5 +; RUN: llvm-dis %t5.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT +; RUN: llvm-dis %t5.1.5.precodegen.bc -o - | FileCheck %s --check-prefix=CODEGEN2 +; Check that gFoo and gBar were eliminated from source module together +; with corresponsing stores +; RUN: llvm-dis %t5.2.5.precodegen.bc -o - | FileCheck %s --check-prefix=CODEGEN2-SRC ; IMPORT: @gFoo.llvm.0 = internal unnamed_addr global i32 1, align 4 ; IMPORT-NEXT: @gBar = internal local_unnamed_addr global i32 2, align 4 @@ -41,6 +64,16 @@ ; IMPORT2: @gBar = available_externally dso_local local_unnamed_addr global i32 2, align 4 +; CODEGEN2: i32 @main2 +; CODEGEN2-NEXT: %1 = tail call i32 @rand() +; CODEGEN2-NEXT: %2 = tail call i32 @rand() +; CODEGEN2-NEXT: ret i32 0 + +; CODEGEN2-SRC: void @baz() +; CODEGEN2-SRC-NEXT: %1 = tail call i32 @rand() +; CODEGEN2-SRC-NEXT: %2 = tail call i32 @rand() +; CODEGEN2-SRC-NEXT: ret void + target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" target triple = "x86_64-pc-linux-gnu" @@ -54,6 +87,13 @@ define i32 @main() local_unnamed_addr { ret i32 %add } +define i32 @main2() local_unnamed_addr { + tail call void @baz() + ret i32 0 +} + declare i32 @foo(...) local_unnamed_addr declare i32 @bar(...) local_unnamed_addr + +declare void @baz() local_unnamed_addr diff --git a/llvm/test/ThinLTO/X86/load-store-caching.ll b/llvm/test/ThinLTO/X86/load-store-caching.ll new file mode 100644 index 0000000..9ca9c40 --- /dev/null +++ b/llvm/test/ThinLTO/X86/load-store-caching.ll @@ -0,0 +1,26 @@ +; Test that instruction operands from loads are not cached when +; processing stores. Reference from @foo to @obj should not be +; readonly or writeonly + +; RUN: opt -module-summary %s -o %t.bc +; RUN: llvm-dis %t.bc -o - | FileCheck %s + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +%struct.S = type { %struct.Derived* } +%struct.Derived = type { i32 } +%struct.Base = type { i32 } + +@obj = dso_local local_unnamed_addr global %struct.S zeroinitializer, align 8 + +define dso_local %struct.Base* @foo() local_unnamed_addr { +entry: + %0 = load %struct.Base*, %struct.Base** bitcast (%struct.S* @obj to %struct.Base**), align 8 + store %struct.Base* null, %struct.Base** bitcast (%struct.S* @obj to %struct.Base**), align 8 + ret %struct.Base* %0 +} + +; CHECK: ^0 = module: +; CHECK-NEXT: ^1 = gv: (name: "obj", summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), varFlags: (readonly: 1, writeonly: 1)))) ; guid = +; CHECK-NEXT: ^2 = gv: (name: "foo", summaries: (function: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 3, refs: (^1)))) ; guid = diff --git a/llvm/test/ThinLTO/X86/writeonly.ll b/llvm/test/ThinLTO/X86/writeonly.ll new file mode 100644 index 0000000..7c2af6d --- /dev/null +++ b/llvm/test/ThinLTO/X86/writeonly.ll @@ -0,0 +1,41 @@ +; Checks that we optimize writeonly variables and corresponding stores using llvm-lto +; -stats requires asserts +; REQUIRES: asserts + +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/index-const-prop.ll -o %t2.bc +; RUN: llvm-lto -thinlto-action=thinlink -o %t3.index.bc %t1.bc %t2.bc + +; Check that we optimize write-only variables +; RUN: llvm-lto -thinlto-action=import -exported-symbol=main %t1.bc -thinlto-index=%t3.index.bc -o %t1.imported.bc -stats 2>&1 | FileCheck %s --check-prefix=STATS +; RUN: llvm-dis %t1.imported.bc -o - | FileCheck %s --check-prefix=IMPORT +; RUN: llvm-lto -thinlto-action=optimize %t1.imported.bc -o - | llvm-dis - -o - | FileCheck %s --check-prefix=OPTIMIZE + +; IMPORT: @gFoo.llvm.0 = internal unnamed_addr global i32 1, align 4, !dbg !0 +; IMPORT-NEXT: @gBar = internal local_unnamed_addr global i32 2, align 4, !dbg !5 +; IMPORT: !DICompileUnit({{.*}}) + +; STATS: 2 module-summary-index - Number of live global variables marked write only + +; Check that we've optimized out variables and corresponding stores +; OPTIMIZE-NOT: gFoo +; OPTIMIZE-NOT: gBar +; OPTIMIZE: i32 @main +; OPTIMIZE-NEXT: %1 = tail call i32 @rand() +; OPTIMIZE-NEXT: %2 = tail call i32 @rand() +; OPTIMIZE-NEXT: ret i32 0 + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@gBar = external global i32 + +; Should not be counted in the stats of live write only variables since it is +; dead and will be dropped anyway. +@gDead = internal unnamed_addr global i32 1, align 4 + +define i32 @main() local_unnamed_addr { + tail call void @baz() + ret i32 0 +} +declare void @baz() local_unnamed_addr diff --git a/llvm/test/ThinLTO/X86/writeonly2.ll b/llvm/test/ThinLTO/X86/writeonly2.ll new file mode 100644 index 0000000..b1e53ba --- /dev/null +++ b/llvm/test/ThinLTO/X86/writeonly2.ll @@ -0,0 +1,50 @@ +; Check that we optimize out writeonly variables and corresponding stores. +; This test uses llvm-lto2 + +; RUN: opt -module-summary %s -o %t1.bc +; RUN: opt -module-summary %p/Inputs/index-const-prop.ll -o %t2.bc +; RUN: llvm-lto2 run %t1.bc %t2.bc -save-temps \ +; RUN: -r=%t2.bc,foo,pl \ +; RUN: -r=%t2.bc,bar,pl \ +; RUN: -r=%t2.bc,baz,pl \ +; RUN: -r=%t2.bc,rand, \ +; RUN: -r=%t2.bc,gBar,pl \ +; RUN: -r=%t1.bc,main,plx \ +; RUN: -r=%t1.bc,baz, \ +; RUN: -r=%t1.bc,gBar, \ +; RUN: -o %t3 +; RUN: llvm-dis %t3.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT +; RUN: llvm-dis %t3.1.5.precodegen.bc -o - | FileCheck %s --check-prefix=CODEGEN +; Check that gFoo and gBar were eliminated from source module together +; with corresponsing stores +; RUN: llvm-dis %t3.2.5.precodegen.bc -o - | FileCheck %s --check-prefix=CODEGEN-SRC + +; IMPORT: @gFoo.llvm.0 = internal unnamed_addr global i32 1, align 4 +; IMPORT-NEXT: @gBar = internal local_unnamed_addr global i32 2, align 4 +; IMPORT: !DICompileUnit({{.*}}) + +; CODEGEN-NOT: gFoo +; CODEGEN-NOT: gBar +; CODEGEN: i32 @main +; CODEGEN-NEXT: %1 = tail call i32 @rand() +; CODEGEN-NEXT: %2 = tail call i32 @rand() +; CODEGEN-NEXT: ret i32 0 + +; CODEGEN-SRC-NOT: gFoo +; CODEGEN-SRC-NOT: gBar +; CODEGEN-SRC: void @baz() +; CODEGEN-SRC-NEXT: %1 = tail call i32 @rand() +; CODEGEN-SRC-NEXT: %2 = tail call i32 @rand() +; CODEGEN-SRC-NEXT: ret void + +target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +; We should be able to link external definition of gBar to its declaration +@gBar = external global i32 + +define i32 @main() local_unnamed_addr { + tail call void @baz() + ret i32 0 +} +declare void @baz() local_unnamed_addr |