LLVM 23.0.0git
WaitingOnGraph.h
Go to the documentation of this file.
1//===------ WaitingOnGraph.h - ORC symbol dependence graph ------*- C++ -*-===//
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// Defines WaitingOnGraph and related utilities.
10//
11//===----------------------------------------------------------------------===//
12#ifndef LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPH_H
13#define LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPH_H
14
15#include "llvm/ADT/DenseMap.h"
16#include "llvm/ADT/DenseSet.h"
17#include "llvm/ADT/STLExtras.h"
20
21#include <algorithm>
22#include <vector>
23
24namespace llvm::orc::detail {
25
26class WaitingOnGraphTest;
27
28/// WaitingOnGraph class template.
29///
30/// This type is intended to provide efficient dependence tracking for Symbols
31/// in an ORC program.
32///
33/// WaitingOnGraph models a directed graph with four partitions:
34/// 1. Not-yet-emitted nodes: Nodes identified as waited-on in an emit
35/// operation.
36/// 2. Emitted nodes: Nodes emitted and waiting on some non-empty set of
37/// other nodes.
38/// 3. Ready nodes: Nodes emitted and not waiting on any other nodes
39/// (either because they weren't waiting on any nodes when they were
40/// emitted, or because all transitively waited-on nodes have since
41/// been emitted).
42/// 4. Failed nodes: Nodes that have been marked as failed-to-emit, and
43/// nodes that were found to transitively wait-on some failed node.
44///
45/// Nodes are added to the graph by *emit* and *fail* operations.
46///
47/// The *emit* operation takes a bipartite *local dependence graph* as an
48/// argument and returns...
49/// a. the set of nodes (both existing and newly added from the local
50/// dependence graph) whose waiting-on set is the empty set, and...
51/// b. the set of newly added nodes that are found to depend on failed
52/// nodes.
53///
54/// The *fail* operation takes a set of failed nodes and returns the set of
55/// Emitted nodes that were waiting on the failed nodes.
56///
57/// The concrete representation adopts several approaches for efficiency:
58///
59/// 1. Only *Emitted* and *Not-yet-emitted* nodes are represented explicitly.
60/// *Ready* and *Failed* nodes are represented by the values returned by the
61/// GetExternalStateFn argument to *emit*.
62///
63/// 2. Labels are (*Container*, *Element*) pairs that are intended to represent
64/// ORC symbols (ORC uses types Container = JITDylib,
65/// Element = NonOwningSymbolStringPtr). The internal representation of the
66/// graph is optimized on the assumption that there are many more Elements
67/// (symbol names) than Containers (JITDylibs) used to construct the labels.
68/// (Consider for example the common case where most JIT'd code is placed in
69/// a single "main" JITDylib).
70///
71/// 3. The data structure stores *SuperNodes* which have multiple labels. This
72/// reduces the number of nodes and edges in the graph in the common case
73/// where many JIT symbols have the same set of dependencies. SuperNodes are
74/// coalesced when their dependence sets become equal.
75///
76/// 4. The *simplify* method can be applied to an initial *local dependence
77/// graph* (as a list of SuperNodes) to eliminate any internal dependence
78/// relationships that would have to be propagated internally by *emit*.
79/// Access to the WaitingOnGraph is assumed to be guarded by a mutex (ORC
80/// will access it from multiple threads) so this allows some pre-processing
81/// to be performed outside the mutex.
82template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
83 friend class WaitingOnGraphTest;
84
85public:
86 using ContainerId = ContainerIdT;
87 using ElementId = ElementIdT;
90
91 class SuperNode {
92 friend class WaitingOnGraph;
93 friend class WaitingOnGraphTest;
94
95 public:
97 : Defs(std::move(Defs)), Deps(std::move(Deps)) {}
98 ContainerElementsMap &defs() { return Defs; }
99 const ContainerElementsMap &defs() const { return Defs; }
100 ContainerElementsMap &deps() { return Deps; }
101 const ContainerElementsMap &deps() const { return Deps; }
102
103 private:
106 };
107
108private:
109 using ElemToSuperNodeMap =
111
112 using SuperNodeDepsMap = DenseMap<SuperNode *, DenseSet<SuperNode *>>;
113
114 class Coalescer {
115 public:
116 std::unique_ptr<SuperNode> addOrCreateSuperNode(ContainerElementsMap Defs,
118 auto H = getHash(Deps);
119 if (auto *ExistingSN = findCanonicalSuperNode(H, Deps)) {
120 for (auto &[Container, Elems] : Defs) {
121 auto &DstCElems = ExistingSN->Defs[Container];
122 [[maybe_unused]] size_t ExpectedSize =
123 DstCElems.size() + Elems.size();
124 DstCElems.insert(Elems.begin(), Elems.end());
125 assert(DstCElems.size() == ExpectedSize);
126 }
127 return nullptr;
128 }
129
130 auto NewSN =
131 std::make_unique<SuperNode>(std::move(Defs), std::move(Deps));
132 CanonicalSNs[H].push_back(NewSN.get());
133 assert(!SNHashes.count(NewSN.get()));
134 SNHashes[NewSN.get()] = H;
135 return NewSN;
136 }
137
138 void coalesce(std::vector<std::unique_ptr<SuperNode>> &SNs,
139 ElemToSuperNodeMap &ElemToSN) {
140 for (size_t I = 0; I != SNs.size();) {
141 auto &SN = SNs[I];
142 assert(!SNHashes.count(SN.get()) &&
143 "Elements of SNs should be new to the coalescer");
144 auto H = getHash(SN->Deps);
145 if (auto *CanonicalSN = findCanonicalSuperNode(H, SN->Deps)) {
146 for (auto &[Container, Elems] : SN->Defs) {
147 CanonicalSN->Defs[Container].insert(Elems.begin(), Elems.end());
148 auto &ContainerElemToSN = ElemToSN[Container];
149 for (auto &Elem : Elems)
150 ContainerElemToSN[Elem] = CanonicalSN;
151 }
152 std::swap(SN, SNs.back());
153 SNs.pop_back();
154 } else {
155 CanonicalSNs[H].push_back(SN.get());
156 SNHashes[SN.get()] = H;
157 ++I;
158 }
159 }
160 }
161
162 /// Remove all coalescing information.
163 ///
164 /// This resets the Coalescer to the same functional state that it was
165 /// constructed in.
166 void clear() {
167 CanonicalSNs.clear();
168 SNHashes.clear();
169 }
170
171 /// Remove the given node from the Coalescer.
172 void erase(SuperNode *SN) {
173 hash_code H;
174
175 {
176 // Look up hash. We expect to find it in SNHashes.
177 auto I = SNHashes.find(SN);
178 assert(I != SNHashes.end() && "SN not tracked by coalescer");
179 H = I->second;
180 SNHashes.erase(I);
181 }
182
183 // Now remove from CanonicalSNs.
184 auto I = CanonicalSNs.find(H);
185 assert(I != CanonicalSNs.end() && "Hash not in CanonicalSNs");
186 auto &SNs = I->second;
187
188 size_t J = 0;
189 for (; J != SNs.size(); ++J)
190 if (SNs[J] == SN)
191 break;
192
193 assert(J < SNs.size() && "SN not in CanonicalSNs map");
194 std::swap(SNs[J], SNs.back());
195 SNs.pop_back();
196
197 if (SNs.empty())
198 CanonicalSNs.erase(I);
199 }
200
201 private:
202 hash_code getHash(const ContainerElementsMap &M) {
203 SmallVector<ContainerId> SortedContainers;
204 SortedContainers.reserve(M.size());
205 for (auto &[Container, Elems] : M)
206 SortedContainers.push_back(Container);
207 llvm::sort(SortedContainers);
208 hash_code Hash(0);
209 for (auto &Container : SortedContainers) {
210 auto &ContainerElems = M.at(Container);
211 SmallVector<ElementId> SortedElems(ContainerElems.begin(),
212 ContainerElems.end());
213 llvm::sort(SortedElems);
214 Hash = hash_combine(Hash, Container, hash_combine_range(SortedElems));
215 }
216 return Hash;
217 }
218
219 SuperNode *findCanonicalSuperNode(hash_code H,
220 const ContainerElementsMap &M) {
221 for (auto *SN : CanonicalSNs[H])
222 if (SN->Deps == M)
223 return SN;
224 return nullptr;
225 }
226
227 DenseMap<hash_code, SmallVector<SuperNode *>> CanonicalSNs;
228 DenseMap<SuperNode *, hash_code> SNHashes;
229 };
230
231public:
232 /// Build SuperNodes from (definition-set, dependence-set) pairs.
233 ///
234 /// Coalesces definition-sets with identical dependence-sets.
236 public:
238 if (Defs.empty())
239 return;
240 // Remove any self-reference.
242 for (auto &[Container, Elems] : Defs) {
243 assert(!Elems.empty() && "Defs for container must not be empty");
244 auto I = Deps.find(Container);
245 if (I == Deps.end())
246 continue;
247 auto &DepsForContainer = I->second;
248 for (auto &Elem : Elems)
249 DepsForContainer.erase(Elem);
250 if (DepsForContainer.empty())
251 ToRemove.push_back(Container);
252 }
253 for (auto &Container : ToRemove)
254 Deps.erase(Container);
255 if (auto SN = C.addOrCreateSuperNode(std::move(Defs), std::move(Deps)))
256 SNs.push_back(std::move(SN));
257 }
258 std::vector<std::unique_ptr<SuperNode>> takeSuperNodes() {
259 C.clear();
260 return std::move(SNs);
261 }
262
263 private:
264 Coalescer C;
265 std::vector<std::unique_ptr<SuperNode>> SNs;
266 };
267
268 class SimplifyResult {
269 friend class WaitingOnGraph;
270 friend class WaitingOnGraphTest;
271
272 public:
273 const std::vector<std::unique_ptr<SuperNode>> &superNodes() const {
274 return SNs;
275 }
276
277 private:
278 SimplifyResult(std::vector<std::unique_ptr<SuperNode>> SNs,
279 ElemToSuperNodeMap ElemToSN)
280 : SNs(std::move(SNs)), ElemToSN(std::move(ElemToSN)) {}
281 std::vector<std::unique_ptr<SuperNode>> SNs;
282 ElemToSuperNodeMap ElemToSN;
283 };
284
285 /// Preprocess a list of SuperNodes to remove all intra-SN dependencies.
286 static SimplifyResult simplify(std::vector<std::unique_ptr<SuperNode>> SNs) {
287 // Build ElemToSN map.
288 ElemToSuperNodeMap ElemToSN;
289 for (auto &SN : SNs) {
290 for (auto &[Container, Elements] : SN->Defs) {
291 auto &ContainerElemToSN = ElemToSN[Container];
292 for (auto &E : Elements)
293 ContainerElemToSN[E] = SN.get();
294 }
295 }
296
297 SuperNodeDepsMap SuperNodeDeps;
298 hoistDeps(SuperNodeDeps, SNs, ElemToSN);
299 propagateDeps(SuperNodeDeps);
300
301 // Pre-coalesce nodes.
302 Coalescer().coalesce(SNs, ElemToSN);
303
304 return {std::move(SNs), std::move(ElemToSN)};
305 }
306
307 struct EmitResult {
308 std::vector<std::unique_ptr<SuperNode>> Ready;
309 std::vector<std::unique_ptr<SuperNode>> Failed;
310 };
311
312 enum class ExternalState { None, Ready, Failed };
313
314 /// Add the given SuperNodes to the graph, returning any SuperNodes that
315 /// move to the Ready or Failed states as a result.
316 /// The GetExternalState function is used to represent SuperNodes that have
317 /// already become Ready or Failed (since such nodes are not explicitly
318 /// represented in the graph).
319 template <typename GetExternalStateFn>
320 EmitResult emit(SimplifyResult SR, GetExternalStateFn &&GetExternalState) {
321 auto NewSNs = std::move(SR.SNs);
322 auto ElemToNewSN = std::move(SR.ElemToSN);
323
324 // First process any dependencies on nodes with external state.
325 auto FailedSNs = processExternalDeps(NewSNs, GetExternalState);
326
327 // Collect the PendingSNs whose dep sets are about to be modified.
328 std::vector<std::unique_ptr<SuperNode>> ModifiedPendingSNs;
329 for (size_t I = 0; I != PendingSNs.size();) {
330 auto &SN = PendingSNs[I];
331 bool Remove = false;
332 for (auto &[Container, Elems] : SN->Deps) {
333 auto I = ElemToNewSN.find(Container);
334 if (I == ElemToNewSN.end())
335 continue;
336 for (auto Elem : Elems) {
337 if (I->second.contains(Elem)) {
338 Remove = true;
339 break;
340 }
341 }
342 if (Remove)
343 break;
344 }
345 if (Remove) {
346 ModifiedPendingSNs.push_back(std::move(SN));
347 std::swap(SN, PendingSNs.back());
348 PendingSNs.pop_back();
349 } else
350 ++I;
351 }
352
353 // Remove cycles from the graphs.
354 SuperNodeDepsMap SuperNodeDeps;
355 hoistDeps(SuperNodeDeps, ModifiedPendingSNs, ElemToNewSN);
356
357 // If SN's deps are about to be modified then remove it from the coalescer.
358 for (auto &SN : ModifiedPendingSNs)
359 CoalesceToPendingSNs.erase(SN.get());
360
361 hoistDeps(SuperNodeDeps, NewSNs, ElemToPendingSN);
362 propagateDeps(SuperNodeDeps);
363
364 propagateFailures(FailedSNs, SuperNodeDeps);
365
366 // Process supernodes. Pending first, since we'll update PendingSNs when we
367 // incorporate NewSNs.
368 std::vector<std::unique_ptr<SuperNode>> ReadyNodes, FailedNodes;
369 processReadyOrFailed(ModifiedPendingSNs, ReadyNodes, FailedNodes,
370 SuperNodeDeps, FailedSNs, &ElemToPendingSN);
371 processReadyOrFailed(NewSNs, ReadyNodes, FailedNodes, SuperNodeDeps,
372 FailedSNs, nullptr);
373
374 CoalesceToPendingSNs.coalesce(ModifiedPendingSNs, ElemToPendingSN);
375 CoalesceToPendingSNs.coalesce(NewSNs, ElemToPendingSN);
376
377 // Integrate remaining ModifiedPendingSNs and NewSNs into PendingSNs.
378 for (auto &SN : ModifiedPendingSNs)
379 PendingSNs.push_back(std::move(SN));
380
381 // Update ElemToPendingSN for the remaining elements.
382 for (auto &SN : NewSNs) {
383 for (auto &[Container, Elems] : SN->Defs) {
384 auto &Row = ElemToPendingSN[Container];
385 for (auto &Elem : Elems)
386 Row[Elem] = SN.get();
387 }
388 PendingSNs.push_back(std::move(SN));
389 }
390
391 return {std::move(ReadyNodes), std::move(FailedNodes)};
392 }
393
394 /// Identify the given symbols as Failed.
395 /// The elements of the Failed map will not be included in the returned
396 /// result, so clients should take whatever actions are needed to mark
397 /// this as failed in their external representation.
398 std::vector<std::unique_ptr<SuperNode>>
400 std::vector<std::unique_ptr<SuperNode>> FailedSNs;
401
402 for (size_t I = 0; I != PendingSNs.size();) {
403 auto &PendingSN = PendingSNs[I];
404 bool FailPendingSN = false;
405 for (auto &[Container, Elems] : PendingSN->Deps) {
406 if (FailPendingSN)
407 break;
408 auto I = Failed.find(Container);
409 if (I == Failed.end())
410 continue;
411 for (auto &Elem : Elems) {
412 if (I->second.count(Elem)) {
413 FailPendingSN = true;
414 break;
415 }
416 }
417 }
418 if (FailPendingSN) {
419 FailedSNs.push_back(std::move(PendingSN));
420 PendingSN = std::move(PendingSNs.back());
421 PendingSNs.pop_back();
422 } else
423 ++I;
424 }
425
426 for (auto &FailedSN : FailedSNs)
427 CoalesceToPendingSNs.erase(FailedSN.get());
428
429 for (auto &SN : FailedSNs) {
430 for (auto &[Container, Elems] : SN->Defs) {
431 assert(ElemToPendingSN.count(Container));
432 auto &CElems = ElemToPendingSN[Container];
433 for (auto &Elem : Elems)
434 CElems.erase(Elem);
435 if (CElems.empty())
436 ElemToPendingSN.erase(Container);
437 }
438 }
439
440 return FailedSNs;
441 }
442
444 bool AllGood = true;
445 auto ErrLog = [&]() -> raw_ostream & {
446 AllGood = false;
447 return Log;
448 };
449
450 size_t DefCount = 0;
451 for (auto &PendingSN : PendingSNs) {
452 if (PendingSN->Deps.empty())
453 ErrLog() << "Pending SN " << PendingSN.get() << " has empty dep set.\n";
454 else {
455 bool BadElem = false;
456 for (auto &[Container, Elems] : PendingSN->Deps) {
457 auto I = ElemToPendingSN.find(Container);
458 if (I == ElemToPendingSN.end())
459 continue;
460 if (Elems.empty())
461 ErrLog() << "Pending SN " << PendingSN.get()
462 << " has dependence map entry for " << Container
463 << " with empty element set.\n";
464 for (auto &Elem : Elems) {
465 if (I->second.count(Elem)) {
466 ErrLog() << "Pending SN " << PendingSN.get()
467 << " has dependence on emitted element ( " << Container
468 << ", " << Elem << ")\n";
469 BadElem = true;
470 break;
471 }
472 }
473 if (BadElem)
474 break;
475 }
476 }
477
478 for (auto &[Container, Elems] : PendingSN->Defs) {
479 if (Elems.empty())
480 ErrLog() << "Pending SN " << PendingSN.get()
481 << " has def map entry for " << Container
482 << " with empty element set.\n";
483 DefCount += Elems.size();
484 auto I = ElemToPendingSN.find(Container);
485 if (I == ElemToPendingSN.end())
486 ErrLog() << "Pending SN " << PendingSN.get() << " has "
487 << Elems.size() << " defs in container " << Container
488 << " not covered by ElemsToPendingSN.\n";
489 else {
490 for (auto &Elem : Elems) {
491 auto J = I->second.find(Elem);
492 if (J == I->second.end())
493 ErrLog() << "Pending SN " << PendingSN.get() << " has element ("
494 << Container << ", " << Elem
495 << ") not covered by ElemsToPendingSN.\n";
496 else if (J->second != PendingSN.get())
497 ErrLog() << "ElemToPendingSN value invalid for (" << Container
498 << ", " << Elem << ")\n";
499 }
500 }
501 }
502 }
503
504 size_t DefCount2 = 0;
505 for (auto &[Container, Elems] : ElemToPendingSN)
506 DefCount2 += Elems.size();
507
508 assert(DefCount2 >= DefCount);
509 if (DefCount2 != DefCount)
510 ErrLog() << "ElemToPendingSN contains extra elements.\n";
511
512 return AllGood;
513 }
514
515private:
516 // Replace individual dependencies with supernode dependencies.
517 static void hoistDeps(SuperNodeDepsMap &SuperNodeDeps,
518 std::vector<std::unique_ptr<SuperNode>> &SNs,
519 ElemToSuperNodeMap &ElemToSN) {
520 // For all SNs...
521 for (auto &SN : SNs) {
522 SmallVector<ContainerId> ContainersToRemove;
523 for (auto &[DepContainer, DepElems] : SN->Deps) {
524
525 // Check ElemToSN to see if any other SuperNodes define elements in
526 // DepContainer. If not then bail out early.
527 auto I = ElemToSN.find(DepContainer);
528 if (I == ElemToSN.end())
529 continue;
530 auto &ContainerElemToSN = I->second;
531
532 // ElemToSN includes SuperNodes that define elements in DepContainer.
533 // We need to iterate over ContainerElemToSN or DepElems: we pick the
534 // smaller to minimize the cost.
535 if (ContainerElemToSN.size() < DepElems.size()) {
536 for (auto &[DefElem, DefSN] : ContainerElemToSN)
537 if (DepElems.erase(DefElem) && DefSN != SN.get())
538 SuperNodeDeps[DefSN].insert(SN.get());
539 } else {
540 SmallVector<ElementId> ElemsToRemove;
541 for (auto &DepElem : DepElems) {
542 auto J = ContainerElemToSN.find(DepElem);
543 if (J == ContainerElemToSN.end())
544 continue;
545 ElemsToRemove.push_back(DepElem);
546 SuperNode *DefSN = J->second;
547 if (DefSN != SN.get())
548 SuperNodeDeps[DefSN].insert(SN.get());
549 }
550
551 for (auto &DepElem : ElemsToRemove)
552 DepElems.erase(DepElem);
553 }
554
555 // If DepElems has become empty then add DepContainer to the list of
556 // containers to remove.
557 if (DepElems.empty())
558 ContainersToRemove.push_back(DepContainer);
559 }
560
561 // Remove any containers in SN->Deps that have become empty.
562 for (auto &DepContainer : ContainersToRemove) {
563 assert(SN->Deps.count(DepContainer) && "DepContainer already removed?");
564 assert(SN->Deps[DepContainer].empty() && "DepContainer deps not empty");
565 SN->Deps.erase(DepContainer);
566 }
567 }
568 }
569
570 // Compute transitive closure of deps for each node.
571 static void propagateDeps(SuperNodeDepsMap &SuperNodeDeps) {
572
573 // Early exit for self-contained emits.
574 if (SuperNodeDeps.empty())
575 return;
576
578 Worklist.reserve(SuperNodeDeps.size());
579 for (auto &[SN, SNDependants] : SuperNodeDeps)
580 Worklist.push_back(SN);
581
582 while (true) {
583 DenseSet<SuperNode *> ToVisitNext;
584
585 // TODO: See if topo-sorting worklist improves convergence.
586
587 while (!Worklist.empty()) {
588 auto *SN = Worklist.pop_back_val();
589 auto I = SuperNodeDeps.find(SN);
590 if (I == SuperNodeDeps.end())
591 continue;
592
593 for (auto *DependantSN : I->second) {
594 bool Changed = false;
595 for (auto &[DepContainer, DepElems] : SN->Deps) {
596 auto &DepSNContainerElems = DependantSN->Deps[DepContainer];
597 for (auto &DepElem : DepElems)
598 Changed |= DepSNContainerElems.insert(DepElem).second;
599 }
600 if (Changed)
601 ToVisitNext.insert(DependantSN);
602 }
603 }
604
605 if (ToVisitNext.empty())
606 break;
607
608 Worklist.append(ToVisitNext.begin(), ToVisitNext.end());
609 }
610 }
611
612 static void propagateFailures(DenseSet<SuperNode *> &FailedNodes,
613 SuperNodeDepsMap &SuperNodeDeps) {
614 if (FailedNodes.empty())
615 return;
616
617 SmallVector<SuperNode *> Worklist(FailedNodes.begin(), FailedNodes.end());
618
619 while (!Worklist.empty()) {
620 auto *SN = Worklist.pop_back_val();
621 auto I = SuperNodeDeps.find(SN);
622 if (I == SuperNodeDeps.end())
623 continue;
624
625 for (auto *DependantSN : I->second)
626 if (FailedNodes.insert(DependantSN).second)
627 Worklist.push_back(DependantSN);
628 }
629 }
630
631 template <typename GetExternalStateFn>
632 static DenseSet<SuperNode *>
633 processExternalDeps(std::vector<std::unique_ptr<SuperNode>> &SNs,
634 GetExternalStateFn &GetExternalState) {
635 DenseSet<SuperNode *> FailedSNs;
636 for (auto &SN : SNs) {
637 bool SNHasError = false;
638 SmallVector<ContainerId> ContainersToRemove;
639 for (auto &[Container, Elems] : SN->Deps) {
640 SmallVector<ElementId> ElemToRemove;
641 for (auto &Elem : Elems) {
642 switch (GetExternalState(Container, Elem)) {
644 break;
646 ElemToRemove.push_back(Elem);
647 break;
649 ElemToRemove.push_back(Elem);
650 SNHasError = true;
651 break;
652 }
653 }
654 for (auto &Elem : ElemToRemove)
655 Elems.erase(Elem);
656 if (Elems.empty())
657 ContainersToRemove.push_back(Container);
658 }
659 for (auto &Container : ContainersToRemove)
660 SN->Deps.erase(Container);
661 if (SNHasError)
662 FailedSNs.insert(SN.get());
663 }
664
665 return FailedSNs;
666 }
667
668 void processReadyOrFailed(std::vector<std::unique_ptr<SuperNode>> &SNs,
669 std::vector<std::unique_ptr<SuperNode>> &Ready,
670 std::vector<std::unique_ptr<SuperNode>> &Failed,
671 SuperNodeDepsMap &SuperNodeDeps,
672 const DenseSet<SuperNode *> &FailedSNs,
673 ElemToSuperNodeMap *ElemToSNs) {
674
675 SmallVector<SuperNode *> ToRemoveFromElemToSNs;
676
677 for (size_t I = 0; I != SNs.size();) {
678 auto &SN = SNs[I];
679
680 bool SNFailed = FailedSNs.count(SN.get());
681 bool SNReady = SN->Deps.empty();
682
683 if (SNReady || SNFailed) {
684 if (ElemToSNs)
685 ToRemoveFromElemToSNs.push_back(SN.get());
686 auto &NodeList = SNFailed ? Failed : Ready;
687 NodeList.push_back(std::move(SN));
688 std::swap(SN, SNs.back());
689 SNs.pop_back();
690 } else
691 ++I;
692 }
693
694 // Update ElemToSNs (if passed) to remove elements pointing at SN.
695 for (auto *SN : ToRemoveFromElemToSNs) {
696 for (auto &[Container, Elems] : SN->defs()) {
697 auto &Row = (*ElemToSNs)[Container];
698 for (auto &Elem : Elems)
699 Row.erase(Elem);
700 }
701 }
702 }
703
704 std::vector<std::unique_ptr<SuperNode>> PendingSNs;
705 ElemToSuperNodeMap ElemToPendingSN;
706 Coalescer CoalesceToPendingSNs;
707};
708
709} // namespace llvm::orc::detail
710
711#endif // LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPH_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
ReachingDefInfo InstSet & ToRemove
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
This file defines the DenseMap class.
This file defines the DenseSet and SmallDenseSet classes.
#define I(x, y, z)
Definition MD5.cpp:57
#define H(x, y, z)
Definition MD5.cpp:56
register Register Coalescer
This file contains some templates that are useful if you are working with the STL at all.
This file defines the SmallVector class.
iterator find(const_arg_type_t< KeyT > Val)
Definition DenseMap.h:178
bool erase(const KeyT &Val)
Definition DenseMap.h:330
bool empty() const
Definition DenseMap.h:109
iterator end()
Definition DenseMap.h:81
Implements a dense probed hash-table based set.
Definition DenseSet.h:279
iterator erase(const_iterator CI)
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
const std::vector< std::unique_ptr< SuperNode > > & superNodes() const
Build SuperNodes from (definition-set, dependence-set) pairs.
void add(ContainerElementsMap Defs, ContainerElementsMap Deps)
std::vector< std::unique_ptr< SuperNode > > takeSuperNodes()
const ContainerElementsMap & defs() const
const ContainerElementsMap & deps() const
SuperNode(ContainerElementsMap Defs, ContainerElementsMap Deps)
WaitingOnGraph class template.
std::vector< std::unique_ptr< SuperNode > > fail(const ContainerElementsMap &Failed)
Identify the given symbols as Failed.
EmitResult emit(SimplifyResult SR, GetExternalStateFn &&GetExternalState)
Add the given SuperNodes to the graph, returning any SuperNodes that move to the Ready or Failed stat...
static SimplifyResult simplify(std::vector< std::unique_ptr< SuperNode > > SNs)
Preprocess a list of SuperNodes to remove all intra-SN dependencies.
bool validate(raw_ostream &Log)
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition raw_ostream.h:53
Changed
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
SmallVector< Node, 4 > NodeList
Definition RDFGraph.h:550
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1636
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1917
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition Hashing.h:592
hash_code hash_combine_range(InputIteratorT first, InputIteratorT last)
Compute a hash_code for a sequence of values.
Definition Hashing.h:466
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:870
void swap(llvm::BitVector &LHS, llvm::BitVector &RHS)
Implement std::swap in terms of BitVector swap.
Definition BitVector.h:872
std::vector< std::unique_ptr< SuperNode > > Failed
std::vector< std::unique_ptr< SuperNode > > Ready