Mercurial > hg > CbC > CbC_llvm
comparison lib/IR/ModuleSummaryIndex.cpp @ 148:63bd29f05246
merged
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Wed, 14 Aug 2019 19:46:37 +0900 |
parents | c2174574ed3a |
children |
comparison
equal
deleted
inserted
replaced
146:3fc4d5c3e21e | 148:63bd29f05246 |
---|---|
1 //===-- ModuleSummaryIndex.cpp - Module Summary Index ---------------------===// | 1 //===-- ModuleSummaryIndex.cpp - Module Summary Index ---------------------===// |
2 // | 2 // |
3 // The LLVM Compiler Infrastructure | 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 // | 4 // See https://llvm.org/LICENSE.txt for license information. |
5 // This file is distributed under the University of Illinois Open Source | 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 // License. See LICENSE.TXT for details. | |
7 // | 6 // |
8 //===----------------------------------------------------------------------===// | 7 //===----------------------------------------------------------------------===// |
9 // | 8 // |
10 // This file implements the module index and summary classes for the | 9 // This file implements the module index and summary classes for the |
11 // IR library. | 10 // IR library. |
12 // | 11 // |
13 //===----------------------------------------------------------------------===// | 12 //===----------------------------------------------------------------------===// |
14 | 13 |
15 #include "llvm/IR/ModuleSummaryIndex.h" | 14 #include "llvm/IR/ModuleSummaryIndex.h" |
15 #include "llvm/ADT/SCCIterator.h" | |
16 #include "llvm/ADT/Statistic.h" | |
16 #include "llvm/ADT/StringMap.h" | 17 #include "llvm/ADT/StringMap.h" |
17 #include "llvm/Support/Path.h" | 18 #include "llvm/Support/Path.h" |
19 #include "llvm/Support/raw_ostream.h" | |
18 using namespace llvm; | 20 using namespace llvm; |
21 | |
22 #define DEBUG_TYPE "module-summary-index" | |
23 | |
24 STATISTIC(ReadOnlyLiveGVars, | |
25 "Number of live global variables marked read only"); | |
26 STATISTIC(WriteOnlyLiveGVars, | |
27 "Number of live global variables marked write only"); | |
28 | |
29 FunctionSummary FunctionSummary::ExternalNode = | |
30 FunctionSummary::makeDummyFunctionSummary({}); | |
19 | 31 |
20 bool ValueInfo::isDSOLocal() const { | 32 bool ValueInfo::isDSOLocal() const { |
21 // Need to check all summaries are local in case of hash collisions. | 33 // Need to check all summaries are local in case of hash collisions. |
22 return getSummaryList().size() && | 34 return getSummaryList().size() && |
23 llvm::all_of(getSummaryList(), | 35 llvm::all_of(getSummaryList(), |
24 [](const std::unique_ptr<GlobalValueSummary> &Summary) { | 36 [](const std::unique_ptr<GlobalValueSummary> &Summary) { |
25 return Summary->isDSOLocal(); | 37 return Summary->isDSOLocal(); |
26 }); | 38 }); |
39 } | |
40 | |
41 bool ValueInfo::canAutoHide() const { | |
42 // Can only auto hide if all copies are eligible to auto hide. | |
43 return getSummaryList().size() && | |
44 llvm::all_of(getSummaryList(), | |
45 [](const std::unique_ptr<GlobalValueSummary> &Summary) { | |
46 return Summary->canAutoHide(); | |
47 }); | |
48 } | |
49 | |
50 // Gets the number of readonly and writeonly refs in RefEdgeList | |
51 std::pair<unsigned, unsigned> FunctionSummary::specialRefCounts() const { | |
52 // Here we take advantage of having all readonly and writeonly references | |
53 // located in the end of the RefEdgeList. | |
54 auto Refs = refs(); | |
55 unsigned RORefCnt = 0, WORefCnt = 0; | |
56 int I; | |
57 for (I = Refs.size() - 1; I >= 0 && Refs[I].isWriteOnly(); --I) | |
58 WORefCnt++; | |
59 for (; I >= 0 && Refs[I].isReadOnly(); --I) | |
60 RORefCnt++; | |
61 return {RORefCnt, WORefCnt}; | |
27 } | 62 } |
28 | 63 |
29 // Collect for the given module the list of function it defines | 64 // Collect for the given module the list of function it defines |
30 // (GUID -> Summary). | 65 // (GUID -> Summary). |
31 void ModuleSummaryIndex::collectDefinedFunctionsForModule( | 66 void ModuleSummaryIndex::collectDefinedFunctionsForModule( |
43 GVSummaryMap[GUID] = Summary; | 78 GVSummaryMap[GUID] = Summary; |
44 } | 79 } |
45 } | 80 } |
46 } | 81 } |
47 | 82 |
48 // Collect for each module the list of function it defines (GUID -> Summary). | |
49 void ModuleSummaryIndex::collectDefinedGVSummariesPerModule( | |
50 StringMap<GVSummaryMapTy> &ModuleToDefinedGVSummaries) const { | |
51 for (auto &GlobalList : *this) { | |
52 auto GUID = GlobalList.first; | |
53 for (auto &Summary : GlobalList.second.SummaryList) { | |
54 ModuleToDefinedGVSummaries[Summary->modulePath()][GUID] = Summary.get(); | |
55 } | |
56 } | |
57 } | |
58 | |
59 GlobalValueSummary * | 83 GlobalValueSummary * |
60 ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID, | 84 ModuleSummaryIndex::getGlobalValueSummary(uint64_t ValueGUID, |
61 bool PerModuleIndex) const { | 85 bool PerModuleIndex) const { |
62 auto VI = getValueInfo(ValueGUID); | 86 auto VI = getValueInfo(ValueGUID); |
63 assert(VI && "GlobalValue not found in index"); | 87 assert(VI && "GlobalValue not found in index"); |
78 if (isGlobalValueLive(I.get())) | 102 if (isGlobalValueLive(I.get())) |
79 return true; | 103 return true; |
80 return false; | 104 return false; |
81 } | 105 } |
82 | 106 |
107 static void propagateAttributesToRefs(GlobalValueSummary *S) { | |
108 // If reference is not readonly or writeonly then referenced summary is not | |
109 // read/writeonly either. Note that: | |
110 // - All references from GlobalVarSummary are conservatively considered as | |
111 // not readonly or writeonly. Tracking them properly requires more complex | |
112 // analysis then we have now. | |
113 // | |
114 // - AliasSummary objects have no refs at all so this function is a no-op | |
115 // for them. | |
116 for (auto &VI : S->refs()) { | |
117 assert(VI.getAccessSpecifier() == 0 || isa<FunctionSummary>(S)); | |
118 for (auto &Ref : VI.getSummaryList()) | |
119 // If references to alias is not read/writeonly then aliasee | |
120 // is not read/writeonly | |
121 if (auto *GVS = dyn_cast<GlobalVarSummary>(Ref->getBaseObject())) { | |
122 if (!VI.isReadOnly()) | |
123 GVS->setReadOnly(false); | |
124 if (!VI.isWriteOnly()) | |
125 GVS->setWriteOnly(false); | |
126 } | |
127 } | |
128 } | |
129 | |
130 // Do the access attribute propagation in combined index. | |
131 // The goal of attribute propagation is internalization of readonly (RO) | |
132 // or writeonly (WO) variables. To determine which variables are RO or WO | |
133 // and which are not we take following steps: | |
134 // - During analysis we speculatively assign readonly and writeonly | |
135 // attribute to all variables which can be internalized. When computing | |
136 // function summary we also assign readonly or writeonly attribute to a | |
137 // reference if function doesn't modify referenced variable (readonly) | |
138 // or doesn't read it (writeonly). | |
139 // | |
140 // - After computing dead symbols in combined index we do the attribute | |
141 // propagation. During this step we: | |
142 // a. clear RO and WO attributes from variables which are preserved or | |
143 // can't be imported | |
144 // b. clear RO and WO attributes from variables referenced by any global | |
145 // variable initializer | |
146 // c. clear RO attribute from variable referenced by a function when | |
147 // reference is not readonly | |
148 // d. clear WO attribute from variable referenced by a function when | |
149 // reference is not writeonly | |
150 // | |
151 // Because of (c, d) we don't internalize variables read by function A | |
152 // and modified by function B. | |
153 // | |
154 // Internalization itself happens in the backend after import is finished | |
155 // See internalizeGVsAfterImport. | |
156 void ModuleSummaryIndex::propagateAttributes( | |
157 const DenseSet<GlobalValue::GUID> &GUIDPreservedSymbols) { | |
158 for (auto &P : *this) | |
159 for (auto &S : P.second.SummaryList) { | |
160 if (!isGlobalValueLive(S.get())) | |
161 // We don't examine references from dead objects | |
162 continue; | |
163 | |
164 // Global variable can't be marked read/writeonly if it is not eligible | |
165 // to import since we need to ensure that all external references get | |
166 // a local (imported) copy. It also can't be marked read/writeonly if | |
167 // it or any alias (since alias points to the same memory) are preserved | |
168 // or notEligibleToImport, since either of those means there could be | |
169 // writes (or reads in case of writeonly) that are not visible (because | |
170 // preserved means it could have external to DSO writes or reads, and | |
171 // notEligibleToImport means it could have writes or reads via inline | |
172 // assembly leading it to be in the @llvm.*used). | |
173 if (auto *GVS = dyn_cast<GlobalVarSummary>(S->getBaseObject())) | |
174 // Here we intentionally pass S.get() not GVS, because S could be | |
175 // an alias. | |
176 if (!canImportGlobalVar(S.get()) || | |
177 GUIDPreservedSymbols.count(P.first)) { | |
178 GVS->setReadOnly(false); | |
179 GVS->setWriteOnly(false); | |
180 } | |
181 propagateAttributesToRefs(S.get()); | |
182 } | |
183 if (llvm::AreStatisticsEnabled()) | |
184 for (auto &P : *this) | |
185 if (P.second.SummaryList.size()) | |
186 if (auto *GVS = dyn_cast<GlobalVarSummary>( | |
187 P.second.SummaryList[0]->getBaseObject())) | |
188 if (isGlobalValueLive(GVS)) { | |
189 if (GVS->maybeReadOnly()) | |
190 ReadOnlyLiveGVars++; | |
191 if (GVS->maybeWriteOnly()) | |
192 WriteOnlyLiveGVars++; | |
193 } | |
194 } | |
195 | |
196 // TODO: write a graphviz dumper for SCCs (see ModuleSummaryIndex::exportToDot) | |
197 // then delete this function and update its tests | |
198 LLVM_DUMP_METHOD | |
199 void ModuleSummaryIndex::dumpSCCs(raw_ostream &O) { | |
200 for (scc_iterator<ModuleSummaryIndex *> I = | |
201 scc_begin<ModuleSummaryIndex *>(this); | |
202 !I.isAtEnd(); ++I) { | |
203 O << "SCC (" << utostr(I->size()) << " node" << (I->size() == 1 ? "" : "s") | |
204 << ") {\n"; | |
205 for (const ValueInfo V : *I) { | |
206 FunctionSummary *F = nullptr; | |
207 if (V.getSummaryList().size()) | |
208 F = cast<FunctionSummary>(V.getSummaryList().front().get()); | |
209 O << " " << (F == nullptr ? "External" : "") << " " << utostr(V.getGUID()) | |
210 << (I.hasLoop() ? " (has loop)" : "") << "\n"; | |
211 } | |
212 O << "}\n"; | |
213 } | |
214 } | |
215 | |
83 namespace { | 216 namespace { |
84 struct Attributes { | 217 struct Attributes { |
85 void add(const Twine &Name, const Twine &Value, | 218 void add(const Twine &Name, const Twine &Value, |
86 const Twine &Comment = Twine()); | 219 const Twine &Comment = Twine()); |
220 void addComment(const Twine &Comment); | |
87 std::string getAsString() const; | 221 std::string getAsString() const; |
88 | 222 |
89 std::vector<std::string> Attrs; | 223 std::vector<std::string> Attrs; |
90 std::string Comments; | 224 std::string Comments; |
91 }; | 225 }; |
103 std::string A = Name.str(); | 237 std::string A = Name.str(); |
104 A += "=\""; | 238 A += "=\""; |
105 A += Value.str(); | 239 A += Value.str(); |
106 A += "\""; | 240 A += "\""; |
107 Attrs.push_back(A); | 241 Attrs.push_back(A); |
242 addComment(Comment); | |
243 } | |
244 | |
245 void Attributes::addComment(const Twine &Comment) { | |
108 if (!Comment.isTriviallyEmpty()) { | 246 if (!Comment.isTriviallyEmpty()) { |
109 if (Comments.empty()) | 247 if (Comments.empty()) |
110 Comments = " // "; | 248 Comments = " // "; |
111 else | 249 else |
112 Comments += ", "; | 250 Comments += ", "; |
156 return "<unknown>"; | 294 return "<unknown>"; |
157 } | 295 } |
158 | 296 |
159 static std::string fflagsToString(FunctionSummary::FFlags F) { | 297 static std::string fflagsToString(FunctionSummary::FFlags F) { |
160 auto FlagValue = [](unsigned V) { return V ? '1' : '0'; }; | 298 auto FlagValue = [](unsigned V) { return V ? '1' : '0'; }; |
161 char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), | 299 char FlagRep[] = {FlagValue(F.ReadNone), FlagValue(F.ReadOnly), |
162 FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), 0}; | 300 FlagValue(F.NoRecurse), FlagValue(F.ReturnDoesNotAlias), |
301 FlagValue(F.NoInline), 0}; | |
163 | 302 |
164 return FlagRep; | 303 return FlagRep; |
165 } | 304 } |
166 | 305 |
167 // Get string representation of function instruction count and flags. | 306 // Get string representation of function instruction count and flags. |
172 | 311 |
173 return std::string("inst: ") + std::to_string(FS->instCount()) + | 312 return std::string("inst: ") + std::to_string(FS->instCount()) + |
174 ", ffl: " + fflagsToString(FS->fflags()); | 313 ", ffl: " + fflagsToString(FS->fflags()); |
175 } | 314 } |
176 | 315 |
316 static std::string getNodeVisualName(GlobalValue::GUID Id) { | |
317 return std::string("@") + std::to_string(Id); | |
318 } | |
319 | |
177 static std::string getNodeVisualName(const ValueInfo &VI) { | 320 static std::string getNodeVisualName(const ValueInfo &VI) { |
178 return VI.name().empty() ? std::string("@") + std::to_string(VI.getGUID()) | 321 return VI.name().empty() ? getNodeVisualName(VI.getGUID()) : VI.name().str(); |
179 : VI.name().str(); | |
180 } | 322 } |
181 | 323 |
182 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) { | 324 static std::string getNodeLabel(const ValueInfo &VI, GlobalValueSummary *GVS) { |
183 if (isa<AliasSummary>(GVS)) | 325 if (isa<AliasSummary>(GVS)) |
184 return getNodeVisualName(VI); | 326 return getNodeVisualName(VI); |
195 | 337 |
196 // Write definition of external node, which doesn't have any | 338 // Write definition of external node, which doesn't have any |
197 // specific module associated with it. Typically this is function | 339 // specific module associated with it. Typically this is function |
198 // or variable defined in native object or library. | 340 // or variable defined in native object or library. |
199 static void defineExternalNode(raw_ostream &OS, const char *Pfx, | 341 static void defineExternalNode(raw_ostream &OS, const char *Pfx, |
200 const ValueInfo &VI) { | 342 const ValueInfo &VI, GlobalValue::GUID Id) { |
201 auto StrId = std::to_string(VI.getGUID()); | 343 auto StrId = std::to_string(Id); |
202 OS << " " << StrId << " [label=\"" << getNodeVisualName(VI) | 344 OS << " " << StrId << " [label=\""; |
203 << "\"]; // defined externally\n"; | 345 |
204 } | 346 if (VI) { |
205 | 347 OS << getNodeVisualName(VI); |
206 void ModuleSummaryIndex::exportToDot(raw_ostream& OS) const { | 348 } else { |
349 OS << getNodeVisualName(Id); | |
350 } | |
351 OS << "\"]; // defined externally\n"; | |
352 } | |
353 | |
354 static bool hasReadOnlyFlag(const GlobalValueSummary *S) { | |
355 if (auto *GVS = dyn_cast<GlobalVarSummary>(S)) | |
356 return GVS->maybeReadOnly(); | |
357 return false; | |
358 } | |
359 | |
360 static bool hasWriteOnlyFlag(const GlobalValueSummary *S) { | |
361 if (auto *GVS = dyn_cast<GlobalVarSummary>(S)) | |
362 return GVS->maybeWriteOnly(); | |
363 return false; | |
364 } | |
365 | |
366 void ModuleSummaryIndex::exportToDot(raw_ostream &OS) const { | |
207 std::vector<Edge> CrossModuleEdges; | 367 std::vector<Edge> CrossModuleEdges; |
208 DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap; | 368 DenseMap<GlobalValue::GUID, std::vector<uint64_t>> NodeMap; |
209 StringMap<GVSummaryMapTy> ModuleToDefinedGVS; | 369 using GVSOrderedMapTy = std::map<GlobalValue::GUID, GlobalValueSummary *>; |
370 std::map<StringRef, GVSOrderedMapTy> ModuleToDefinedGVS; | |
210 collectDefinedGVSummariesPerModule(ModuleToDefinedGVS); | 371 collectDefinedGVSummariesPerModule(ModuleToDefinedGVS); |
211 | 372 |
212 // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required, | 373 // Get node identifier in form MXXX_<GUID>. The MXXX prefix is required, |
213 // because we may have multiple linkonce functions summaries. | 374 // because we may have multiple linkonce functions summaries. |
214 auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) { | 375 auto NodeId = [](uint64_t ModId, GlobalValue::GUID Id) { |
215 return ModId == (uint64_t)-1 ? std::to_string(Id) | 376 return ModId == (uint64_t)-1 ? std::to_string(Id) |
216 : std::string("M") + std::to_string(ModId) + | 377 : std::string("M") + std::to_string(ModId) + |
217 "_" + std::to_string(Id); | 378 "_" + std::to_string(Id); |
218 }; | 379 }; |
219 | 380 |
220 auto DrawEdge = [&](const char *Pfx, int SrcMod, GlobalValue::GUID SrcId, | 381 auto DrawEdge = [&](const char *Pfx, uint64_t SrcMod, GlobalValue::GUID SrcId, |
221 int DstMod, GlobalValue::GUID DstId, int TypeOrHotness) { | 382 uint64_t DstMod, GlobalValue::GUID DstId, |
222 // 0 corresponds to alias edge, 1 to ref edge, 2 to call with unknown | 383 int TypeOrHotness) { |
223 // hotness, ... | 384 // 0 - alias |
224 TypeOrHotness += 2; | 385 // 1 - reference |
386 // 2 - constant reference | |
387 // 3 - writeonly reference | |
388 // Other value: (hotness - 4). | |
389 TypeOrHotness += 4; | |
225 static const char *EdgeAttrs[] = { | 390 static const char *EdgeAttrs[] = { |
226 " [style=dotted]; // alias", | 391 " [style=dotted]; // alias", |
227 " [style=dashed]; // ref", | 392 " [style=dashed]; // ref", |
393 " [style=dashed,color=forestgreen]; // const-ref", | |
394 " [style=dashed,color=violetred]; // writeOnly-ref", | |
228 " // call (hotness : Unknown)", | 395 " // call (hotness : Unknown)", |
229 " [color=blue]; // call (hotness : Cold)", | 396 " [color=blue]; // call (hotness : Cold)", |
230 " // call (hotness : None)", | 397 " // call (hotness : None)", |
231 " [color=brown]; // call (hotness : Hot)", | 398 " [color=brown]; // call (hotness : Hot)", |
232 " [style=bold,color=red]; // call (hotness : Critical)"}; | 399 " [style=bold,color=red]; // call (hotness : Critical)"}; |
237 << EdgeAttrs[TypeOrHotness] << "\n"; | 404 << EdgeAttrs[TypeOrHotness] << "\n"; |
238 }; | 405 }; |
239 | 406 |
240 OS << "digraph Summary {\n"; | 407 OS << "digraph Summary {\n"; |
241 for (auto &ModIt : ModuleToDefinedGVS) { | 408 for (auto &ModIt : ModuleToDefinedGVS) { |
242 auto ModId = getModuleId(ModIt.first()); | 409 auto ModId = getModuleId(ModIt.first); |
243 OS << " // Module: " << ModIt.first() << "\n"; | 410 OS << " // Module: " << ModIt.first << "\n"; |
244 OS << " subgraph cluster_" << std::to_string(ModId) << " {\n"; | 411 OS << " subgraph cluster_" << std::to_string(ModId) << " {\n"; |
245 OS << " style = filled;\n"; | 412 OS << " style = filled;\n"; |
246 OS << " color = lightgrey;\n"; | 413 OS << " color = lightgrey;\n"; |
247 OS << " label = \"" << sys::path::filename(ModIt.first()) << "\";\n"; | 414 OS << " label = \"" << sys::path::filename(ModIt.first) << "\";\n"; |
248 OS << " node [style=filled,fillcolor=lightblue];\n"; | 415 OS << " node [style=filled,fillcolor=lightblue];\n"; |
249 | 416 |
250 auto &GVSMap = ModIt.second; | 417 auto &GVSMap = ModIt.second; |
251 auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) { | 418 auto Draw = [&](GlobalValue::GUID IdFrom, GlobalValue::GUID IdTo, int Hotness) { |
252 if (!GVSMap.count(IdTo)) { | 419 if (!GVSMap.count(IdTo)) { |
265 } else if (isa<AliasSummary>(SummaryIt.second)) { | 432 } else if (isa<AliasSummary>(SummaryIt.second)) { |
266 A.add("style", "dotted,filled", "alias"); | 433 A.add("style", "dotted,filled", "alias"); |
267 A.add("shape", "box"); | 434 A.add("shape", "box"); |
268 } else { | 435 } else { |
269 A.add("shape", "Mrecord", "variable"); | 436 A.add("shape", "Mrecord", "variable"); |
437 if (Flags.Live && hasReadOnlyFlag(SummaryIt.second)) | |
438 A.addComment("immutable"); | |
439 if (Flags.Live && hasWriteOnlyFlag(SummaryIt.second)) | |
440 A.addComment("writeOnly"); | |
270 } | 441 } |
442 if (Flags.DSOLocal) | |
443 A.addComment("dsoLocal"); | |
444 if (Flags.CanAutoHide) | |
445 A.addComment("canAutoHide"); | |
271 | 446 |
272 auto VI = getValueInfo(SummaryIt.first); | 447 auto VI = getValueInfo(SummaryIt.first); |
273 A.add("label", getNodeLabel(VI, SummaryIt.second)); | 448 A.add("label", getNodeLabel(VI, SummaryIt.second)); |
274 if (!Flags.Live) | 449 if (!Flags.Live) |
275 A.add("fillcolor", "red", "dead"); | 450 A.add("fillcolor", "red", "dead"); |
282 OS << " // Edges:\n"; | 457 OS << " // Edges:\n"; |
283 | 458 |
284 for (auto &SummaryIt : GVSMap) { | 459 for (auto &SummaryIt : GVSMap) { |
285 auto *GVS = SummaryIt.second; | 460 auto *GVS = SummaryIt.second; |
286 for (auto &R : GVS->refs()) | 461 for (auto &R : GVS->refs()) |
287 Draw(SummaryIt.first, R.getGUID(), -1); | 462 Draw(SummaryIt.first, R.getGUID(), |
463 R.isWriteOnly() ? -1 : (R.isReadOnly() ? -2 : -3)); | |
288 | 464 |
289 if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) { | 465 if (auto *AS = dyn_cast_or_null<AliasSummary>(SummaryIt.second)) { |
290 auto AliaseeOrigId = AS->getAliasee().getOriginalName(); | 466 Draw(SummaryIt.first, AS->getAliaseeGUID(), -4); |
291 auto AliaseeId = getGUIDFromOriginalID(AliaseeOrigId); | |
292 | |
293 Draw(SummaryIt.first, AliaseeId ? AliaseeId : AliaseeOrigId, -2); | |
294 continue; | 467 continue; |
295 } | 468 } |
296 | 469 |
297 if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second)) | 470 if (auto *FS = dyn_cast_or_null<FunctionSummary>(SummaryIt.second)) |
298 for (auto &CGEdge : FS->calls()) | 471 for (auto &CGEdge : FS->calls()) |
304 | 477 |
305 OS << " // Cross-module edges:\n"; | 478 OS << " // Cross-module edges:\n"; |
306 for (auto &E : CrossModuleEdges) { | 479 for (auto &E : CrossModuleEdges) { |
307 auto &ModList = NodeMap[E.Dst]; | 480 auto &ModList = NodeMap[E.Dst]; |
308 if (ModList.empty()) { | 481 if (ModList.empty()) { |
309 defineExternalNode(OS, " ", getValueInfo(E.Dst)); | 482 defineExternalNode(OS, " ", getValueInfo(E.Dst), E.Dst); |
310 // Add fake module to the list to draw an edge to an external node | 483 // Add fake module to the list to draw an edge to an external node |
311 // in the loop below. | 484 // in the loop below. |
312 ModList.push_back(-1); | 485 ModList.push_back(-1); |
313 } | 486 } |
314 for (auto DstMod : ModList) | 487 for (auto DstMod : ModList) |