1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-22 10:42:39 +01:00

[ThinLTO] Attempt to recommit r365188 after alignment fix

llvm-svn: 365215
This commit is contained in:
Eugene Leviant 2019-07-05 15:25:05 +00:00
parent 3aa131e8d6
commit 1cec5950f5
28 changed files with 594 additions and 191 deletions

View File

@ -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) {}
GVarFlags(bool ReadOnly, bool WriteOnly)
: MaybeReadOnly(ReadOnly), MaybeWriteOnly(WriteOnly) {}
unsigned ReadOnly : 1;
// 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

View File

@ -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));
}

View File

@ -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;
}

View File

@ -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]);

View File

@ -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()) {

View File

@ -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 << ")";

View File

@ -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 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
// - 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.
//
// 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;
}

View File

@ -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);
}
}

View File

@ -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;

View File

@ -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");
}

View File

@ -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)))))

View File

@ -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

View File

@ -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/>

View File

@ -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

View File

@ -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>

View File

@ -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>

View File

@ -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'

View File

@ -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>

View File

@ -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{{.*}}'

View File

@ -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>

View File

@ -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'

View File

@ -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 =

View File

@ -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

View File

@ -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
}

View File

@ -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

View File

@ -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 =

View File

@ -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

View File

@ -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