diff --git a/llvm/include/llvm/IR/ModuleSummaryIndex.h b/llvm/include/llvm/IR/ModuleSummaryIndex.h index a81338e576a9..c931842fd465 100644 --- a/llvm/include/llvm/IR/ModuleSummaryIndex.h +++ b/llvm/include/llvm/IR/ModuleSummaryIndex.h @@ -162,8 +162,7 @@ using GlobalValueSummaryMapTy = /// Struct that holds a reference to a particular GUID in a global value /// summary. struct ValueInfo { - enum Flags { HaveGV = 1, ReadOnly = 2, WriteOnly = 4 }; - PointerIntPair + PointerIntPair RefAndFlags; ValueInfo() = default; @@ -189,33 +188,9 @@ struct ValueInfo { : getRef()->second.U.Name; } - 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); - } + bool haveGVs() const { return RefAndFlags.getInt() & 0x1; } + bool isReadOnly() const { return RefAndFlags.getInt() & 0x2; } + void setReadOnly() { RefAndFlags.setInt(RefAndFlags.getInt() | 0x2); } const GlobalValueSummaryMapTy::value_type *getRef() const { return RefAndFlags.getPointer(); @@ -609,8 +584,8 @@ public: std::move(TypeTestAssumeConstVCalls), std::move(TypeCheckedLoadConstVCalls)}); } - // Gets the number of readonly and writeonly refs in RefEdgeList - std::pair specialRefCounts() const; + // Gets the number of immutable refs in RefEdgeList + unsigned immutableRefCount() const; /// Check if this is a function summary. static bool classof(const GlobalValueSummary *GVS) { @@ -738,12 +713,9 @@ using VTableFuncList = std::vector; /// Global variable summary information to aid decisions and /// implementation of importing. /// -/// 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. +/// Global variable summary has extra flag, telling if it is +/// modified during the program run or not. This affects ThinLTO +/// internalization class GlobalVarSummary : public GlobalValueSummary { private: /// For vtable definitions this holds the list of functions and @@ -752,14 +724,9 @@ private: public: struct GVarFlags { - GVarFlags(bool ReadOnly, bool WriteOnly) - : MaybeReadOnly(ReadOnly), MaybeWriteOnly(WriteOnly) {} + GVarFlags(bool ReadOnly = false) : ReadOnly(ReadOnly) {} - // 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; + unsigned ReadOnly : 1; } VarFlags; GlobalVarSummary(GVFlags Flags, GVarFlags VarFlags, @@ -773,10 +740,8 @@ public: } GVarFlags varflags() const { return VarFlags; } - 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 setReadOnly(bool RO) { VarFlags.ReadOnly = RO; } + bool isReadOnly() const { return VarFlags.ReadOnly; } void setVTableFuncs(VTableFuncList Funcs) { assert(!VTableFuncs); @@ -1347,7 +1312,7 @@ public: void dumpSCCs(raw_ostream &OS); /// Analyze index and detect unmodified globals - void propagateAttributes(const DenseSet &PreservedSymbols); + void propagateConstants(const DenseSet &PreservedSymbols); }; /// GraphTraits definition to build SCC for the index diff --git a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp index e25eb290a665..914561f0f3f1 100644 --- a/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp +++ b/llvm/lib/Analysis/ModuleSummaryAnalysis.cpp @@ -231,13 +231,6 @@ static bool isNonVolatileLoad(const Instruction *I) { return false; } -static bool isNonVolatileStore(const Instruction *I) { - if (const auto *SI = dyn_cast(I)) - return !SI->isVolatile(); - - return false; -} - static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, const Function &F, BlockFrequencyInfo *BFI, ProfileSummaryInfo *PSI, DominatorTree &DT, @@ -252,7 +245,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 CallGraphEdges; - SetVector RefEdges, LoadRefEdges, StoreRefEdges; + SetVector RefEdges; SetVector TypeTests; SetVector TypeTestAssumeVCalls, TypeCheckedLoadVCalls; @@ -265,7 +258,6 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, // list. findRefEdges(Index, &F, RefEdges, Visited); std::vector NonVolatileLoads; - std::vector NonVolatileStores; bool HasInlineAsmMaybeReferencingInternal = false; for (const BasicBlock &BB : F) @@ -273,34 +265,12 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, if (isa(I)) continue; ++NumInsts; - // 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(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(Stored)) - findRefEdges(Index, U, RefEdges, Visited); - continue; - } + if (isNonVolatileLoad(&I)) { + // Postpone processing of non-volatile load instructions + // See comments below + Visited.insert(&I); + NonVolatileLoads.push_back(&I); + continue; } findRefEdges(Index, &I, RefEdges, Visited); auto CS = ImmutableCallSite(&I); @@ -391,61 +361,24 @@ static void computeFunctionSummary(ModuleSummaryIndex &Index, const Module &M, } } - std::vector Refs; - if (IsThinLTO) { - auto AddRefEdges = [&](const std::vector &Instrs, - SetVector &Edges, - SmallPtrSet &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 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) + // 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 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) 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()) @@ -593,11 +526,10 @@ static void computeVariableSummary(ModuleSummaryIndex &Index, } } - // Don't mark variables we won't be able to internalize as read/write-only. - bool CanBeInternalized = + // Don't mark variables we won't be able to internalize as read-only. + GlobalVarSummary::GVarFlags VarFlags( !V.hasComdat() && !V.hasAppendingLinkage() && !V.isInterposable() && - !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass(); - GlobalVarSummary::GVarFlags VarFlags(CanBeInternalized, CanBeInternalized); + !V.hasAvailableExternallyLinkage() && !V.hasDLLExportStorageClass()); auto GVarSummary = llvm::make_unique(Flags, VarFlags, RefEdges.takeVector()); if (NonRenamableLocal) @@ -715,7 +647,7 @@ ModuleSummaryIndex llvm::buildModuleSummaryIndex( } else { std::unique_ptr Summary = llvm::make_unique( - GVFlags, GlobalVarSummary::GVarFlags(false, false), + GVFlags, GlobalVarSummary::GVarFlags(), ArrayRef{}); Index.addGlobalValueSummary(*GV, std::move(Summary)); } diff --git a/llvm/lib/AsmParser/LLParser.cpp b/llvm/lib/AsmParser/LLParser.cpp index 085f3bd2999e..9bfce74eb0b0 100644 --- a/llvm/lib/AsmParser/LLParser.cpp +++ b/llvm/lib/AsmParser/LLParser.cpp @@ -7860,13 +7860,9 @@ 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. @@ -8096,8 +8092,7 @@ 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, - /* WriteOnly */ false); + GlobalVarSummary::GVarFlags GVarFlags(/*ReadOnly*/ false); std::vector Refs; VTableFuncList VTableFuncs; if (ParseToken(lltok::colon, "expected ':' here") || @@ -8438,11 +8433,10 @@ bool LLParser::ParseOptionalRefs(std::vector &Refs) { VContexts.push_back(VC); } while (EatIfPresent(lltok::comma)); - // Sort value contexts so that ones with writeonly - // and readonly ValueInfo are at the end of VContexts vector. - // See FunctionSummary::specialRefCounts() + // Sort value contexts so that ones with readonly ValueInfo are at the end + // of VContexts vector. This is needed to match immutableRefCount() behavior. llvm::sort(VContexts, [](const ValueContext &VC1, const ValueContext &VC2) { - return VC1.VI.getAccessSpecifier() < VC2.VI.getAccessSpecifier(); + return VC1.VI.isReadOnly() < VC2.VI.isReadOnly(); }); IdToIndexMapType IdToIndexMap; @@ -8760,41 +8754,24 @@ bool LLParser::ParseGVFlags(GlobalValueSummary::GVFlags &GVFlags) { } /// GVarFlags -/// ::= 'varFlags' ':' '(' 'readonly' ':' Flag -/// ',' 'writeonly' ':' Flag ')' +/// ::= 'varFlags' ':' '(' 'readonly' ':' 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::lparen, "expected '(' here") || + ParseToken(lltok::kw_readonly, "expected 'readonly' here") || + ParseToken(lltok::colon, "expected ':' here")) return true; - auto ParseRest = [this](unsigned int &Val) { - Lex.Lex(); - if (ParseToken(lltok::colon, "expected ':'")) - return true; - return ParseFlag(Val); - }; + ParseFlag(Flag); + GVarFlags.ReadOnly = Flag; - 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"); + if (ParseToken(lltok::rparen, "expected ')' here")) + return true; + return false; } /// ModuleReference @@ -8817,9 +8794,7 @@ bool LLParser::ParseModuleReference(StringRef &ModulePath) { /// GVReference /// ::= SummaryID bool LLParser::ParseGVReference(ValueInfo &VI, unsigned &GVId) { - bool WriteOnly = false, ReadOnly = EatIfPresent(lltok::kw_readonly); - if (!ReadOnly) - WriteOnly = EatIfPresent(lltok::kw_writeonly); + bool ReadOnly = EatIfPresent(lltok::kw_readonly); if (ParseToken(lltok::SummaryID, "expected GV ID")) return true; @@ -8834,7 +8809,5 @@ 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 a339f6eb710a..affbe4cee796 100644 --- a/llvm/lib/Bitcode/Reader/BitcodeReader.cpp +++ b/llvm/lib/Bitcode/Reader/BitcodeReader.cpp @@ -984,8 +984,7 @@ 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, - (RawFlags & 0x2) ? true : false); + return GlobalVarSummary::GVarFlags((RawFlags & 0x1) ? true : false); } static GlobalValue::VisibilityTypes getDecodedVisibility(unsigned Val) { @@ -5684,16 +5683,10 @@ void ModuleSummaryIndexBitcodeReader::parseTypeIdCompatibleVtableSummaryRecord( parseTypeIdCompatibleVtableInfo(Record, Slot, TypeId); } -static void setSpecialRefs(std::vector &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) +static void setImmutableRefs(std::vector &Refs, unsigned Count) { + // Read-only refs are in the end of the refs list. + for (unsigned RefNo = Refs.size() - Count; RefNo < Refs.size(); ++RefNo) Refs[RefNo].setReadOnly(); - for (; RefNo < Refs.size(); ++RefNo) - Refs[RefNo].setWriteOnly(); } // Eagerly parse the entire summary block. This populates the GlobalValueSummary @@ -5720,9 +5713,9 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { } const uint64_t Version = Record[0]; const bool IsOldProfileFormat = Version == 1; - if (Version < 1 || Version > 7) + if (Version < 1 || Version > 6) return error("Invalid summary version " + Twine(Version) + - ". Version should be in the range [1-7]."); + ". Version should be in the range [1-6]."); Record.clear(); // Keep around the last seen summary to be used when we see an optional @@ -5821,19 +5814,15 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { unsigned InstCount = Record[2]; uint64_t RawFunFlags = 0; unsigned NumRefs = Record[3]; - unsigned NumRORefs = 0, NumWORefs = 0; + unsigned NumImmutableRefs = 0; int RefListStartIndex = 4; if (Version >= 4) { RawFunFlags = Record[3]; NumRefs = Record[4]; RefListStartIndex = 5; if (Version >= 5) { - NumRORefs = Record[5]; + NumImmutableRefs = Record[5]; RefListStartIndex = 6; - if (Version >= 7) { - NumWORefs = Record[6]; - RefListStartIndex = 7; - } } } @@ -5853,7 +5842,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { std::vector Calls = makeCallList( ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, HasRelBF); - setSpecialRefs(Refs, NumRORefs, NumWORefs); + setImmutableRefs(Refs, NumImmutableRefs); auto FS = llvm::make_unique( Flags, InstCount, getDecodedFFlags(RawFunFlags), /*EntryCount=*/0, std::move(Refs), std::move(Calls), std::move(PendingTypeTests), @@ -5904,8 +5893,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { unsigned ValueID = Record[0]; uint64_t RawFlags = Record[1]; unsigned RefArrayStart = 2; - GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, - /* WriteOnly */ false); + GlobalVarSummary::GVarFlags GVF; auto Flags = getDecodedGVSummaryFlags(RawFlags, Version); if (Version >= 5) { GVF = getDecodedGVarFlags(Record[2]); @@ -5962,7 +5950,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { uint64_t RawFunFlags = 0; uint64_t EntryCount = 0; unsigned NumRefs = Record[4]; - unsigned NumRORefs = 0, NumWORefs = 0; + unsigned NumImmutableRefs = 0; int RefListStartIndex = 5; if (Version >= 4) { @@ -5970,19 +5958,13 @@ 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; - } } - NumRORefs = Record[RefListStartIndex - NumRORefsOffset]; + NumImmutableRefs = Record[RefListStartIndex - 1]; } NumRefs = Record[NumRefsIndex]; } @@ -5998,7 +5980,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { ArrayRef(Record).slice(CallGraphEdgeStartIndex), IsOldProfileFormat, HasProfile, false); ValueInfo VI = getValueInfoFromValueId(ValueID).first; - setSpecialRefs(Refs, NumRORefs, NumWORefs); + setImmutableRefs(Refs, NumImmutableRefs); auto FS = llvm::make_unique( Flags, InstCount, getDecodedFFlags(RawFunFlags), EntryCount, std::move(Refs), std::move(Edges), std::move(PendingTypeTests), @@ -6045,8 +6027,7 @@ Error ModuleSummaryIndexBitcodeReader::parseEntireSummary(unsigned ID) { uint64_t ModuleId = Record[1]; uint64_t RawFlags = Record[2]; unsigned RefArrayStart = 3; - GlobalVarSummary::GVarFlags GVF(/* ReadOnly */ false, - /* WriteOnly */ false); + GlobalVarSummary::GVarFlags GVF; 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 547889f82c72..3812481139e1 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.MaybeReadOnly | (Flags.MaybeWriteOnly << 1); + uint64_t RawFlags = Flags.ReadOnly; return RawFlags; } @@ -3632,13 +3632,11 @@ void ModuleBitcodeWriterBase::writePerModuleFunctionSummaryRecord( FunctionSummary *FS = cast(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(SpecialRefCnts.first); // rorefcnt - NameVals.push_back(SpecialRefCnts.second); // worefcnt + NameVals.push_back(FS->immutableRefCount()); for (auto &RI : FS->refs()) NameVals.push_back(VE.getValueID(RI.getValue())); @@ -3712,7 +3710,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 = 7; +static const uint64_t INDEX_VERSION = 6; /// Emit the per-module summary section alongside the rest of /// the module's bitcode. @@ -3754,8 +3752,7 @@ 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)); // rorefcnt - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt // numrefs x valueid, n x (valueid, hotness) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3772,8 +3769,7 @@ 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)); // rorefcnt - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt // numrefs x valueid, n x (valueid [, rel_block_freq]) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3905,8 +3901,7 @@ 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)); // rorefcnt - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt // numrefs x valueid, n x (valueid) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -3922,8 +3917,7 @@ 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)); // rorefcnt - Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // worefcnt + Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 4)); // immutablerefcnt // numrefs x valueid, n x (valueid, hotness) Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array)); Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); @@ -4025,24 +4019,20 @@ void IndexBitcodeWriter::writeCombinedGlobalValueSummary() { // Fill in below NameVals.push_back(0); // numrefs - NameVals.push_back(0); // rorefcnt - NameVals.push_back(0); // worefcnt + NameVals.push_back(0); // immutablerefcnt - unsigned Count = 0, RORefCnt = 0, WORefCnt = 0; + unsigned Count = 0, ImmutableRefCnt = 0; for (auto &RI : FS->refs()) { auto RefValueId = getValueId(RI.getGUID()); if (!RefValueId) continue; NameVals.push_back(*RefValueId); if (RI.isReadOnly()) - RORefCnt++; - else if (RI.isWriteOnly()) - WORefCnt++; + ImmutableRefCnt++; Count++; } NameVals[6] = Count; - NameVals[7] = RORefCnt; - NameVals[8] = WORefCnt; + NameVals[7] = ImmutableRefCnt; bool HasProfileData = false; for (auto &EI : FS->calls()) { diff --git a/llvm/lib/IR/AsmWriter.cpp b/llvm/lib/IR/AsmWriter.cpp index eb5760daecb3..51b1a0339ee6 100644 --- a/llvm/lib/IR/AsmWriter.cpp +++ b/llvm/lib/IR/AsmWriter.cpp @@ -2891,8 +2891,7 @@ void AssemblyWriter::printAliasSummary(const AliasSummary *AS) { } void AssemblyWriter::printGlobalVarSummary(const GlobalVarSummary *GS) { - Out << ", varFlags: (readonly: " << GS->VarFlags.MaybeReadOnly << ", " - << "writeonly: " << GS->VarFlags.MaybeWriteOnly << ")"; + Out << ", varFlags: (readonly: " << GS->VarFlags.ReadOnly << ")"; auto VTableFuncs = GS->vTableFuncs(); if (!VTableFuncs.empty()) { @@ -3102,8 +3101,6 @@ 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 9f347d8da01d..18b7ac09388f 100644 --- a/llvm/lib/IR/ModuleSummaryIndex.cpp +++ b/llvm/lib/IR/ModuleSummaryIndex.cpp @@ -23,8 +23,6 @@ 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({}); @@ -47,18 +45,15 @@ bool ValueInfo::canAutoHide() const { }); } -// 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 +// Gets the number of immutable refs in RefEdgeList +unsigned FunctionSummary::immutableRefCount() const { + // Here we take advantage of having all readonly references // located in the end of the RefEdgeList. auto Refs = refs(); - 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}; + unsigned ImmutableRefCnt = 0; + for (int I = Refs.size() - 1; I >= 0 && Refs[I].isReadOnly(); --I) + ImmutableRefCnt++; + return ImmutableRefCnt; } // Collect for the given module the list of function it defines @@ -104,56 +99,48 @@ bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const { return false; } -static void propagateAttributesToRefs(GlobalValueSummary *S) { - // If reference is not readonly or writeonly then referenced summary is not - // read/writeonly either. Note that: +static void propagateConstantsToRefs(GlobalValueSummary *S) { + // If reference is not readonly then referenced summary is not + // readonly 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. + // not readonly. 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()) { - assert(VI.getAccessSpecifier() == 0 || isa(S)); + if (VI.isReadOnly()) { + // We only mark refs as readonly when computing function summaries on + // analysis phase. + assert(isa(S)); + continue; + } for (auto &Ref : VI.getSummaryList()) - // If references to alias is not read/writeonly then aliasee - // is not read/writeonly - if (auto *GVS = dyn_cast(Ref->getBaseObject())) { - if (!VI.isReadOnly()) - GVS->setReadOnly(false); - if (!VI.isWriteOnly()) - GVS->setWriteOnly(false); - } + // If references to alias is not readonly then aliasee is not readonly + if (auto *GVS = dyn_cast(Ref->getBaseObject())) + GVS->setReadOnly(false); } } -// 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). +// 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. // -// - 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 -// -// Because of (c, d) we don't internalize variables read by function A -// and modified by function B. +// - 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 // // Internalization itself happens in the backend after import is finished -// See internalizeGVsAfterImport. -void ModuleSummaryIndex::propagateAttributes( +// See internalizeImmutableGVs. +void ModuleSummaryIndex::propagateConstants( const DenseSet &GUIDPreservedSymbols) { for (auto &P : *this) for (auto &S : P.second.SummaryList) { @@ -161,36 +148,29 @@ void ModuleSummaryIndex::propagateAttributes( // We don't examine references from dead objects continue; - // 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). + // 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). if (auto *GVS = dyn_cast(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); - GVS->setWriteOnly(false); - } - propagateAttributesToRefs(S.get()); + propagateConstantsToRefs(S.get()); } if (llvm::AreStatisticsEnabled()) for (auto &P : *this) if (P.second.SummaryList.size()) if (auto *GVS = dyn_cast( P.second.SummaryList[0]->getBaseObject())) - if (isGlobalValueLive(GVS)) { - if (GVS->maybeReadOnly()) - ReadOnlyLiveGVars++; - if (GVS->maybeWriteOnly()) - WriteOnlyLiveGVars++; - } + if (isGlobalValueLive(GVS) && GVS->isReadOnly()) + ReadOnlyLiveGVars++; } // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot) @@ -353,13 +333,7 @@ static void defineExternalNode(raw_ostream &OS, const char *Pfx, static bool hasReadOnlyFlag(const GlobalValueSummary *S) { if (auto *GVS = dyn_cast(S)) - return GVS->maybeReadOnly(); - return false; -} - -static bool hasWriteOnlyFlag(const GlobalValueSummary *S) { - if (auto *GVS = dyn_cast(S)) - return GVS->maybeWriteOnly(); + return GVS->isReadOnly(); return false; } @@ -384,14 +358,12 @@ void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { // 0 - alias // 1 - reference // 2 - constant reference - // 3 - writeonly reference - // Other value: (hotness - 4). - TypeOrHotness += 4; + // Other value: (hotness - 3). + TypeOrHotness += 3; 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)", @@ -436,8 +408,6 @@ 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"); @@ -459,11 +429,10 @@ 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.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3)); + Draw(SummaryIt.first, R.getGUID(), R.isReadOnly() ? -1 : -2); if (auto *AS = dyn_cast_or_null(SummaryIt.second)) { - Draw(SummaryIt.first, AS->getAliaseeGUID(), -4); + Draw(SummaryIt.first, AS->getAliaseeGUID(), -3); continue; } diff --git a/llvm/lib/LTO/LTO.cpp b/llvm/lib/LTO/LTO.cpp index 64506890956a..4ed13701aa9e 100644 --- a/llvm/lib/LTO/LTO.cpp +++ b/llvm/lib/LTO/LTO.cpp @@ -192,10 +192,8 @@ void llvm::computeLTOCacheKey( AddUnsigned(VI.isDSOLocal()); AddUsedCfiGlobal(VI.getGUID()); } - if (auto *GVS = dyn_cast(GS)) { - AddUnsigned(GVS->maybeReadOnly()); - AddUnsigned(GVS->maybeWriteOnly()); - } + if (auto *GVS = dyn_cast(GS)) + AddUnsigned(GVS->isReadOnly()); if (auto *FS = dyn_cast(GS)) { for (auto &TT : FS->type_tests()) UsedTypeIds.insert(TT); @@ -373,9 +371,9 @@ void llvm::thinLTOResolvePrevailingInIndex( GUIDPreservedSymbols); } -static bool isWeakObjectWithRWAccess(GlobalValueSummary *GVS) { +static bool isWeakWriteableObject(GlobalValueSummary *GVS) { if (auto *VarSummary = dyn_cast(GVS->getBaseObject())) - return !VarSummary->maybeReadOnly() && !VarSummary->maybeWriteOnly() && + return !VarSummary->isReadOnly() && (VarSummary->linkage() == GlobalValue::WeakODRLinkage || VarSummary->linkage() == GlobalValue::LinkOnceODRLinkage); return false; @@ -396,12 +394,11 @@ 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 both modified - // and read somewhere in the program because reads and writes - // will become inconsistent. - !isWeakObjectWithRWAccess(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 modified somewhere in the + // program because reads and writes will become inconsistent. + !isWeakWriteableObject(S.get())) S->setLinkage(GlobalValue::InternalLinkage); } } diff --git a/llvm/lib/Transforms/IPO/FunctionImport.cpp b/llvm/lib/Transforms/IPO/FunctionImport.cpp index 62c7fbd07223..9207f5fe0ef3 100644 --- a/llvm/lib/Transforms/IPO/FunctionImport.cpp +++ b/llvm/lib/Transforms/IPO/FunctionImport.cpp @@ -850,16 +850,14 @@ void llvm::computeDeadSymbolsWithConstProp( bool ImportEnabled) { computeDeadSymbols(Index, GUIDPreservedSymbols, isPrevailing); if (ImportEnabled) { - Index.propagateAttributes(GUIDPreservedSymbols); + Index.propagateConstants(GUIDPreservedSymbols); } else { - // If import is disabled we should drop read/write-only attribute + // If import is disabled we should drop read-only attribute // from all summaries to prevent internalization. for (auto &P : Index) for (auto &S : P.second.SummaryList) - if (auto *GVS = dyn_cast(S.get())) { + if (auto *GVS = dyn_cast(S.get())) GVS->setReadOnly(false); - GVS->setWriteOnly(false); - } } } @@ -1066,7 +1064,7 @@ static Function *replaceAliasWithAliasee(Module *SrcModule, GlobalAlias *GA) { // Internalize values that we marked with specific attribute // in processGlobalForThinLTO. -static void internalizeGVsAfterImport(Module &M) { +static void internalizeImmutableGVs(Module &M) { for (auto &GV : M.globals()) // Skip GVs which have been converted to declarations // by dropDeadSymbols. @@ -1199,7 +1197,7 @@ Expected FunctionImporter::importFunctions( NumImportedModules++; } - internalizeGVsAfterImport(DestModule); + internalizeImmutableGVs(DestModule); NumImportedFunctions += (ImportedCount - ImportedGVCount); NumImportedGlobalVars += ImportedGVCount; diff --git a/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp b/llvm/lib/Transforms/Utils/FunctionImportUtils.cpp index c9cc0990f237..8e80ee2c5495 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/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. + // 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. // // If global value dead stripping is not enabled in summary then // propagateConstants hasn't been run. We can't internalize GV @@ -241,8 +241,7 @@ void FunctionImportGlobalProcessing::processGlobalForThinLTO(GlobalValue &GV) { if (!GV.isDeclaration() && VI && ImportIndex.withGlobalValueDeadStripping()) { const auto &SL = VI.getSummaryList(); auto *GVS = SL.empty() ? nullptr : dyn_cast(SL[0].get()); - // At this stage "maybe" is "definitely" - if (GVS && (GVS->maybeReadOnly() || GVS->maybeWriteOnly())) + if (GVS && GVS->isReadOnly()) cast(&GV)->addAttribute("thinlto-internalize"); } diff --git a/llvm/test/Assembler/thinlto-summary.ll b/llvm/test/Assembler/thinlto-summary.ll index 23d83e3a2e7c..47444c6d0ee2 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: (writeonly ^14, readonly ^13, ^11)))) +^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)))) ; 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: (writeonly: 0)))) +^14 = gv: (guid: 13, summaries: (variable: (module: ^0, flags: (linkage: external, notEligibleToImport: 0, live: 0, dsoLocal: 1), varFlags: (readonly: 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: (^11, readonly ^13, writeonly ^14)))) +; 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: ^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, 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: ^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: ^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 ac4c703c3512..96c021d895c9 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: "", 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, writeonly: 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), 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, writeonly: 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), 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 e531a078d220..fc3b3bd48777 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: +; CHECK: diff --git a/llvm/test/Bitcode/thinlto-alias.ll b/llvm/test/Bitcode/thinlto-alias.ll index a1412f5269ee..ac82664f834d 100644 --- a/llvm/test/Bitcode/thinlto-alias.ll +++ b/llvm/test/Bitcode/thinlto-alias.ll @@ -21,7 +21,7 @@ ; CHECK-NEXT: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK: ; COMBINED-NEXT: -; COMBINED-NEXT: +; COMBINED-NEXT: ; COMBINED-NEXT: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK-NEXT: diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-cast.ll index e6fc9392745b..d4b4d5491c14 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: +; CHECK-NEXT: ; "another_caller" has only references but no calls. -; CHECK-NEXT: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK-NEXT: diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-pgo.ll index 2bbab0c6bb0d..b9613f732ed8 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: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK: +; COMBINED-NEXT: ; COMBINED-NEXT: ; 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 d444ee7c01fb..e19a4c3d488e 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: ; op4=hot1 op6=cold op8=hot2 op10=hot4 op12=none1 op14=hot3 op16=none2 op18=none3 op20=123 -; CHECK-NEXT: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK: +; COMBINED-NEXT: ; COMBINED_NEXT: diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph-relbf.ll index 7d13ae43b506..ae0d9bebd623 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: ; CHECK: ; op4=none1 op6=hot1 op8=cold1 op10=none2 op12=hot2 op14=cold2 op16=none3 op18=hot3 op20=cold3 op22=123 -; CHECK-NEXT: +; CHECK-NEXT: ; CHECK-NEXT: ; CHECK: +; COMBINED-NEXT: ; COMBINED_NEXT: diff --git a/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll b/llvm/test/Bitcode/thinlto-function-summary-callgraph.ll index 765efcf7b822..0969b849bc01 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: +; COMBINED-NEXT: ; COMBINED-NEXT: ; 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 5a33daa95e34..8211df9c5929 100644 --- a/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll +++ b/llvm/test/Bitcode/thinlto-function-summary-refgraph.ll @@ -41,27 +41,27 @@ ; CHECK: +; CHECK-DAG: ; Function W contains a call to func3 as well as a reference to globalvar: ; op0=W op4=globalvar op5=func3 -; CHECK-DAG: +; CHECK-DAG: ; 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: +; CHECK-DAG: ; 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: +; CHECK-DAG: ; 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: +; CHECK-DAG: ; Variable bar initialization contains address reference to func: ; op0=bar op2=func -; CHECK-DAG: +; CHECK-DAG: ; CHECK: ; CHECK: 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 a5fbbdf1e929..96b7593dd4fe 100644 --- a/llvm/test/ThinLTO/X86/index-const-prop2.ll +++ b/llvm/test/ThinLTO/X86/index-const-prop2.ll @@ -11,10 +11,8 @@ ; 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 @@ -28,32 +26,11 @@ ; 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 %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 +; RUN: -o %t3 +; RUN: llvm-dis %t3.1.3.import.bc -o - | FileCheck %s --check-prefix=IMPORT2 ; IMPORT: @gFoo.llvm.0 = internal unnamed_addr global i32 1, align 4 ; IMPORT-NEXT: @gBar = internal local_unnamed_addr global i32 2, align 4 @@ -64,16 +41,6 @@ ; 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" @@ -87,13 +54,6 @@ 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 deleted file mode 100644 index 9ca9c40fd112..000000000000 --- a/llvm/test/ThinLTO/X86/load-store-caching.ll +++ /dev/null @@ -1,26 +0,0 @@ -; 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 deleted file mode 100644 index 7c2af6d9cce3..000000000000 --- a/llvm/test/ThinLTO/X86/writeonly.ll +++ /dev/null @@ -1,41 +0,0 @@ -; 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 deleted file mode 100644 index b1e53ba47b5c..000000000000 --- a/llvm/test/ThinLTO/X86/writeonly2.ll +++ /dev/null @@ -1,50 +0,0 @@ -; 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