comparison llvm/lib/IR/ModuleSummaryIndex.cpp @ 150:1d019706d866

LLVM10
author anatofuz
date Thu, 13 Feb 2020 15:10:13 +0900
parents
children 0572611fdcc8
comparison
equal deleted inserted replaced
147:c2174574ed3a 150:1d019706d866
1 //===-- ModuleSummaryIndex.cpp - Module Summary Index ---------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file implements the module index and summary classes for the
10 // IR library.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "llvm/IR/ModuleSummaryIndex.h"
15 #include "llvm/ADT/SCCIterator.h"
16 #include "llvm/ADT/Statistic.h"
17 #include "llvm/ADT/StringMap.h"
18 #include "llvm/Support/CommandLine.h"
19 #include "llvm/Support/Path.h"
20 #include "llvm/Support/raw_ostream.h"
21 using namespace llvm;
22
23 #define DEBUG_TYPE "module-summary-index"
24
25 STATISTIC(ReadOnlyLiveGVars,
26 "Number of live global variables marked read only");
27 STATISTIC(WriteOnlyLiveGVars,
28 "Number of live global variables marked write only");
29
30 static cl::opt<bool> PropagateAttrs("propagate-attrs", cl::init(true),
31 cl::Hidden,
32 cl::desc("Propagate attributes in index"));
33
34 // FIXME: Enable again when thin link compile time regressions understood and
35 // addressed
36 static cl::opt<bool> ImportConstantsWithRefs(
37 "import-constants-with-refs", cl::init(false), cl::Hidden,
38 cl::desc("Import constant global variables with references"));
39
40 FunctionSummary FunctionSummary::ExternalNode =
41 FunctionSummary::makeDummyFunctionSummary({});
42
43 bool ValueInfo::isDSOLocal() const {
44 // Need to check all summaries are local in case of hash collisions.
45 return getSummaryList().size() &&
46 llvm::all_of(getSummaryList(),
47 [](const std::unique_ptr<GlobalValueSummary> &Summary) {
48 return Summary->isDSOLocal();
49 });
50 }
51
52 bool ValueInfo::canAutoHide() const {
53 // Can only auto hide if all copies are eligible to auto hide.
54 return getSummaryList().size() &&
55 llvm::all_of(getSummaryList(),
56 [](const std::unique_ptr<GlobalValueSummary> &Summary) {
57 return Summary->canAutoHide();
58 });
59 }
60
61 // Gets the number of readonly and writeonly refs in RefEdgeList
62 std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const {
63 // Here we take advantage of having all readonly and writeonly references
64 // located in the end of the RefEdgeList.
65 auto Refs = refs();
66 unsigned RORefCnt = 0, WORefCnt = 0;
67 int I;
68 for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I)
69 WORefCnt++;
70 for (; I >= 0 && Refs[I].isReadOnly(); --I)
71 RORefCnt++;
72 return {RORefCnt, WORefCnt};
73 }
74
75 constexpr uint64_t ModuleSummaryIndex::BitcodeSummaryVersion;
76
77 // Collect for the given module the list of function it defines
78 // (GUID -> Summary).
79 void ModuleSummaryIndex::collectDefinedFunctionsForModule(
80 StringRef ModulePath, GVSummaryMapTy &GVSummaryMap) const {
81 for (auto &GlobalList : *this) {
82 auto GUID = GlobalList.first;
83 for (auto &GlobSummary : GlobalList.second.SummaryList) {
84 auto *Summary = dyn_cast_or_null<FunctionSummary>(GlobSummary.get());
85 if (!Summary)
86 // Ignore global variable, focus on functions
87 continue;
88 // Ignore summaries from other modules.
89 if (Summary->modulePath() != ModulePath)
90 continue;
91 GVSummaryMap[GUID] = Summary;
92 }
93 }
94 }
95
96 GlobalValueSummary *
97 ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID,
98 bool PerModuleIndex) const {
99 auto VI = getValueInfo(ValueGUID);
100 assert(VI && "GlobalValue not found in index");
101 assert((!PerModuleIndex || VI.getSummaryList().size() == 1) &&
102 "Expected a single entry per global value in per-module index");
103 auto &Summary = VI.getSummaryList()[0];
104 return Summary.get();
105 }
106
107 bool ModuleSummaryIndex::isGUIDLive(GlobalValue::GUID GUID) const {
108 auto VI = getValueInfo(GUID);
109 if (!VI)
110 return true;
111 const auto &SummaryList = VI.getSummaryList();
112 if (SummaryList.empty())
113 return true;
114 for (auto &I : SummaryList)
115 if (isGlobalValueLive(I.get()))
116 return true;
117 return false;
118 }
119
120 static void propagateAttributesToRefs(GlobalValueSummary *S) {
121 // If reference is not readonly or writeonly then referenced summary is not
122 // read/writeonly either. Note that:
123 // - All references from GlobalVarSummary are conservatively considered as
124 // not readonly or writeonly. Tracking them properly requires more complex
125 // analysis then we have now.
126 //
127 // - AliasSummary objects have no refs at all so this function is a no-op
128 // for them.
129 for (auto &VI : S->refs()) {
130 assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S));
131 for (auto &Ref : VI.getSummaryList())
132 // If references to alias is not read/writeonly then aliasee
133 // is not read/writeonly
134 if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) {
135 if (!VI.isReadOnly())
136 GVS->setReadOnly(false);
137 if (!VI.isWriteOnly())
138 GVS->setWriteOnly(false);
139 }
140 }
141 }
142
143 // Do the access attribute propagation in combined index.
144 // The goal of attribute propagation is internalization of readonly (RO)
145 // or writeonly (WO) variables. To determine which variables are RO or WO
146 // and which are not we take following steps:
147 // - During analysis we speculatively assign readonly and writeonly
148 // attribute to all variables which can be internalized. When computing
149 // function summary we also assign readonly or writeonly attribute to a
150 // reference if function doesn't modify referenced variable (readonly)
151 // or doesn't read it (writeonly).
152 //
153 // - After computing dead symbols in combined index we do the attribute
154 // propagation. During this step we:
155 // a. clear RO and WO attributes from variables which are preserved or
156 // can't be imported
157 // b. clear RO and WO attributes from variables referenced by any global
158 // variable initializer
159 // c. clear RO attribute from variable referenced by a function when
160 // reference is not readonly
161 // d. clear WO attribute from variable referenced by a function when
162 // reference is not writeonly
163 //
164 // Because of (c, d) we don't internalize variables read by function A
165 // and modified by function B.
166 //
167 // Internalization itself happens in the backend after import is finished
168 // See internalizeGVsAfterImport.
169 void ModuleSummaryIndex::propagateAttributes(
170 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) {
171 if (!PropagateAttrs)
172 return;
173 for (auto &P : *this)
174 for (auto &S : P.second.SummaryList) {
175 if (!isGlobalValueLive(S.get()))
176 // We don't examine references from dead objects
177 continue;
178
179 // Global variable can't be marked read/writeonly if it is not eligible
180 // to import since we need to ensure that all external references get
181 // a local (imported) copy. It also can't be marked read/writeonly if
182 // it or any alias (since alias points to the same memory) are preserved
183 // or notEligibleToImport, since either of those means there could be
184 // writes (or reads in case of writeonly) that are not visible (because
185 // preserved means it could have external to DSO writes or reads, and
186 // notEligibleToImport means it could have writes or reads via inline
187 // assembly leading it to be in the @llvm.*used).
188 if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject()))
189 // Here we intentionally pass S.get() not GVS, because S could be
190 // an alias. We don't analyze references here, because we have to
191 // know exactly if GV is readonly to do so.
192 if (!canImportGlobalVar(S.get(), /* AnalyzeRefs */ false) ||
193 GUIDPreservedSymbols.count(P.first)) {
194 GVS->setReadOnly(false);
195 GVS->setWriteOnly(false);
196 }
197 propagateAttributesToRefs(S.get());
198 }
199 setWithAttributePropagation();
200 if (llvm::AreStatisticsEnabled())
201 for (auto &P : *this)
202 if (P.second.SummaryList.size())
203 if (auto *GVS = dyn_cast<GlobalVarSummary>(
204 P.second.SummaryList[0]->getBaseObject()))
205 if (isGlobalValueLive(GVS)) {
206 if (GVS->maybeReadOnly())
207 ReadOnlyLiveGVars++;
208 if (GVS->maybeWriteOnly())
209 WriteOnlyLiveGVars++;
210 }
211 }
212
213 bool ModuleSummaryIndex::canImportGlobalVar(GlobalValueSummary *S,
214 bool AnalyzeRefs) const {
215 auto HasRefsPreventingImport = [this](const GlobalVarSummary *GVS) {
216 // We don't analyze GV references during attribute propagation, so
217 // GV with non-trivial initializer can be marked either read or
218 // write-only.
219 // Importing definiton of readonly GV with non-trivial initializer
220 // allows us doing some extra optimizations (like converting indirect
221 // calls to direct).
222 // Definition of writeonly GV with non-trivial initializer should also
223 // be imported. Not doing so will result in:
224 // a) GV internalization in source module (because it's writeonly)
225 // b) Importing of GV declaration to destination module as a result
226 // of promotion.
227 // c) Link error (external declaration with internal definition).
228 // However we do not promote objects referenced by writeonly GV
229 // initializer by means of converting it to 'zeroinitializer'
230 return !(ImportConstantsWithRefs && GVS->isConstant()) &&
231 !isReadOnly(GVS) && !isWriteOnly(GVS) && GVS->refs().size();
232 };
233 auto *GVS = cast<GlobalVarSummary>(S->getBaseObject());
234
235 // Global variable with non-trivial initializer can be imported
236 // if it's readonly. This gives us extra opportunities for constant
237 // folding and converting indirect calls to direct calls. We don't
238 // analyze GV references during attribute propagation, because we
239 // don't know yet if it is readonly or not.
240 return !GlobalValue::isInterposableLinkage(S->linkage()) &&
241 !S->notEligibleToImport() &&
242 (!AnalyzeRefs || !HasRefsPreventingImport(GVS));
243 }
244
245 // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot)
246 // then delete this function and update its tests
247 LLVM_DUMP_METHOD
248 void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) {
249 for (scc_iterator<ModuleSummaryIndex *> I =
250 scc_begin<ModuleSummaryIndex *>(this);
251 !I.isAtEnd(); ++I) {
252 O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s")
253 << ") {\n";
254 for (const ValueInfo &V : *I) {
255 FunctionSummary *F = nullptr;
256 if (V.getSummaryList().size())
257 F = cast<FunctionSummary>(V.getSummaryList().front().get());
258 O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID())
259 << (I.hasLoop() ? " (has loop)" : "") << "\n";
260 }
261 O << "}\n";
262 }
263 }
264
265 namespace {
266 struct Attributes {
267 void add(const Twine &Name, const Twine &Value,
268 const Twine &Comment = Twine());
269 void addComment(const Twine &Comment);
270 std::string getAsString() const;
271
272 std::vector<std::string> Attrs;
273 std::string Comments;
274 };
275
276 struct Edge {
277 uint64_t SrcMod;
278 int Hotness;
279 GlobalValue::GUID Src;
280 GlobalValue::GUID Dst;
281 };
282 }
283
284 void Attributes::add(const Twine &Name, const Twine &Value,
285 const Twine &Comment) {
286 std::string A = Name.str();
287 A += "=\"";
288 A += Value.str();
289 A += "\"";
290 Attrs.push_back(A);
291 addComment(Comment);
292 }
293
294 void Attributes::addComment(const Twine &Comment) {
295 if (!Comment.isTriviallyEmpty()) {
296 if (Comments.empty())
297 Comments = " // ";
298 else
299 Comments += ", ";
300 Comments += Comment.str();
301 }
302 }
303
304 std::string Attributes::getAsString() const {
305 if (Attrs.empty())
306 return "";
307
308 std::string Ret = "[";
309 for (auto &A : Attrs)
310 Ret += A + ",";
311 Ret.pop_back();
312 Ret += "];";
313 Ret += Comments;
314 return Ret;
315 }
316
317 static std::string linkageToString(GlobalValue::LinkageTypes LT) {
318 switch (LT) {
319 case GlobalValue::ExternalLinkage:
320 return "extern";
321 case GlobalValue::AvailableExternallyLinkage:
322 return "av_ext";
323 case GlobalValue::LinkOnceAnyLinkage:
324 return "linkonce";
325 case GlobalValue::LinkOnceODRLinkage:
326 return "linkonce_odr";
327 case GlobalValue::WeakAnyLinkage:
328 return "weak";
329 case GlobalValue::WeakODRLinkage:
330 return "weak_odr";
331 case GlobalValue::AppendingLinkage:
332 return "appending";
333 case GlobalValue::InternalLinkage:
334 return "internal";
335 case GlobalValue::PrivateLinkage:
336 return "private";
337 case GlobalValue::ExternalWeakLinkage:
338 return "extern_weak";
339 case GlobalValue::CommonLinkage:
340 return "common";
341 }
342
343 return "<unknown>";
344 }
345
346 static std::string fflagsToString(FunctionSummary::FFlags F) {
347 auto FlagValue = [](unsigned V) { return V ? '1' : '0'; };
348 char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly),
349 FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias),
350 FlagValue(F.NoInline), FlagValue(F.AlwaysInline), 0};
351
352 return FlagRep;
353 }
354
355 // Get string representation of function instruction count and flags.
356 static std::string getSummaryAttributes(GlobalValueSummary* GVS) {
357 auto *FS = dyn_cast_or_null<FunctionSummary>(GVS);
358 if (!FS)
359 return "";
360
361 return std::string("inst: ") + std::to_string(FS->instCount()) +
362 ", ffl: " + fflagsToString(FS->fflags());
363 }
364
365 static std::string getNodeVisualName(GlobalValue::GUID Id) {
366 return std::string("@") + std::to_string(Id);
367 }
368
369 static std::string getNodeVisualName(const ValueInfo &VI) {
370 return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str();
371 }
372
373 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) {
374 if (isa<AliasSummary>(GVS))
375 return getNodeVisualName(VI);
376
377 std::string Attrs = getSummaryAttributes(GVS);
378 std::string Label =
379 getNodeVisualName(VI) + "|" + linkageToString(GVS->linkage());
380 if (!Attrs.empty())
381 Label += std::string(" (") + Attrs + ")";
382 Label += "}";
383
384 return Label;
385 }
386
387 // Write definition of external node, which doesn't have any
388 // specific module associated with it. Typically this is function
389 // or variable defined in native object or library.
390 static void defineExternalNode(raw_ostream &OS, const char *Pfx,
391 const ValueInfo &VI, GlobalValue::GUID Id) {
392 auto StrId = std::to_string(Id);
393 OS << " " << StrId << " [label=\"";
394
395 if (VI) {
396 OS << getNodeVisualName(VI);
397 } else {
398 OS << getNodeVisualName(Id);
399 }
400 OS << "\"]; // defined externally\n";
401 }
402
403 static bool hasReadOnlyFlag(const GlobalValueSummary *S) {
404 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
405 return GVS->maybeReadOnly();
406 return false;
407 }
408
409 static bool hasWriteOnlyFlag(const GlobalValueSummary *S) {
410 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
411 return GVS->maybeWriteOnly();
412 return false;
413 }
414
415 static bool hasConstantFlag(const GlobalValueSummary *S) {
416 if (auto *GVS = dyn_cast<GlobalVarSummary>(S))
417 return GVS->isConstant();
418 return false;
419 }
420
421 void ModuleSummaryIndex::exportToDot(
422 raw_ostream &OS,
423 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) const {
424 std::vector<Edge> CrossModuleEdges;
425 DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap;
426 using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>;
427 std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS;
428 collectDefinedGVSummariesPerModule(ModuleToDefinedGVS);
429
430 // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required,
431 // because we may have multiple linkonce functions summaries.
432 auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) {
433 return ModId == (uint64_t)-1 ? std::to_string(Id)
434 : std::string("M") + std::to_string(ModId) +
435 "_" + std::to_string(Id);
436 };
437
438 auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId,
439 uint64_t DstMod, GlobalValue::GUID DstId,
440 int TypeOrHotness) {
441 // 0 - alias
442 // 1 - reference
443 // 2 - constant reference
444 // 3 - writeonly reference
445 // Other value: (hotness - 4).
446 TypeOrHotness += 4;
447 static const char *EdgeAttrs[] = {
448 " [style=dotted]; // alias",
449 " [style=dashed]; // ref",
450 " [style=dashed,color=forestgreen]; // const-ref",
451 " [style=dashed,color=violetred]; // writeOnly-ref",
452 " // call (hotness : Unknown)",
453 " [color=blue]; // call (hotness : Cold)",
454 " // call (hotness : None)",
455 " [color=brown]; // call (hotness : Hot)",
456 " [style=bold,color=red]; // call (hotness : Critical)"};
457
458 assert(static_cast<size_t>(TypeOrHotness) <
459 sizeof(EdgeAttrs) / sizeof(EdgeAttrs[0]));
460 OS << Pfx << NodeId(SrcMod, SrcId) << " -> " << NodeId(DstMod, DstId)
461 << EdgeAttrs[TypeOrHotness] << "\n";
462 };
463
464 OS << "digraph Summary {\n";
465 for (auto &ModIt : ModuleToDefinedGVS) {
466 auto ModId = getModuleId(ModIt.first);
467 OS << " // Module: " << ModIt.first << "\n";
468 OS << " subgraph cluster_" << std::to_string(ModId) << " {\n";
469 OS << " style = filled;\n";
470 OS << " color = lightgrey;\n";
471 OS << " label = \"" << sys::path::filename(ModIt.first) << "\";\n";
472 OS << " node [style=filled,fillcolor=lightblue];\n";
473
474 auto &GVSMap = ModIt.second;
475 auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) {
476 if (!GVSMap.count(IdTo)) {
477 CrossModuleEdges.push_back({ModId, Hotness, IdFrom, IdTo});
478 return;
479 }
480 DrawEdge(" ", ModId, IdFrom, ModId, IdTo, Hotness);
481 };
482
483 for (auto &SummaryIt : GVSMap) {
484 NodeMap[SummaryIt.first].push_back(ModId);
485 auto Flags = SummaryIt.second->flags();
486 Attributes A;
487 if (isa<FunctionSummary>(SummaryIt.second)) {
488 A.add("shape", "record", "function");
489 } else if (isa<AliasSummary>(SummaryIt.second)) {
490 A.add("style", "dotted,filled", "alias");
491 A.add("shape", "box");
492 } else {
493 A.add("shape", "Mrecord", "variable");
494 if (Flags.Live && hasReadOnlyFlag(SummaryIt.second))
495 A.addComment("immutable");
496 if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second))
497 A.addComment("writeOnly");
498 if (Flags.Live && hasConstantFlag(SummaryIt.second))
499 A.addComment("constant");
500 }
501 if (Flags.DSOLocal)
502 A.addComment("dsoLocal");
503 if (Flags.CanAutoHide)
504 A.addComment("canAutoHide");
505 if (GUIDPreservedSymbols.count(SummaryIt.first))
506 A.addComment("preserved");
507
508 auto VI = getValueInfo(SummaryIt.first);
509 A.add("label", getNodeLabel(VI, SummaryIt.second));
510 if (!Flags.Live)
511 A.add("fillcolor", "red", "dead");
512 else if (Flags.NotEligibleToImport)
513 A.add("fillcolor", "yellow", "not eligible to import");
514
515 OS << " " << NodeId(ModId, SummaryIt.first) << " " << A.getAsString()
516 << "\n";
517 }
518 OS << " // Edges:\n";
519
520 for (auto &SummaryIt : GVSMap) {
521 auto *GVS = SummaryIt.second;
522 for (auto &R : GVS->refs())
523 Draw(SummaryIt.first, R.getGUID(),
524 R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3));
525
526 if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) {
527 Draw(SummaryIt.first, AS->getAliaseeGUID(), -4);
528 continue;
529 }
530
531 if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second))
532 for (auto &CGEdge : FS->calls())
533 Draw(SummaryIt.first, CGEdge.first.getGUID(),
534 static_cast<int>(CGEdge.second.Hotness));
535 }
536 OS << " }\n";
537 }
538
539 OS << " // Cross-module edges:\n";
540 for (auto &E : CrossModuleEdges) {
541 auto &ModList = NodeMap[E.Dst];
542 if (ModList.empty()) {
543 defineExternalNode(OS, " ", getValueInfo(E.Dst), E.Dst);
544 // Add fake module to the list to draw an edge to an external node
545 // in the loop below.
546 ModList.push_back(-1);
547 }
548 for (auto DstMod : ModList)
549 // The edge representing call or ref is drawn to every module where target
550 // symbol is defined. When target is a linkonce symbol there can be
551 // multiple edges representing a single call or ref, both intra-module and
552 // cross-module. As we've already drawn all intra-module edges before we
553 // skip it here.
554 if (DstMod != E.SrcMod)
555 DrawEdge(" ", E.SrcMod, E.Src, DstMod, E.Dst, E.Hotness);
556 }
557
558 OS << "}";
559 }