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;
88
89 class ElementSet : public DenseSet<ElementId> {
90 friend class ElementSetTest;
91
92 public:
94
95 /// Merge the elements of Other into this set. Returns true if any new
96 /// elements are added.
97 bool merge(const ElementSet &Other, bool AssertNoOverlap = false) {
98 size_t OrigSize = this->size();
99 this->insert(Other.begin(), Other.end());
100 assert((!AssertNoOverlap || this->size() == (OrigSize + Other.size())) &&
101 "merge of overlapping elements");
102 return this->size() != OrigSize;
103 }
104
105 /// Remove all elements in Other from this set. Returns true if any
106 /// elements were removed.
107 bool remove(const ElementSet &Other) {
108 size_t OrigSize = this->size();
109
110 // Early out for empty sets.
111 if (OrigSize == 0 || Other.empty())
112 return false;
113
114 // TODO: Tweak condition to account for SmallVector cost. We may want to
115 // prefer iterating over elements if the size difference is small.
116 if (OrigSize > Other.size()) {
117 for (auto &Elem : Other)
118 this->erase(Elem);
119 } else {
121 for (auto &Elem : *this)
122 if (Other.count(Elem))
123 ToRemove.push_back(Elem);
124 for (auto &Elem : ToRemove)
125 this->erase(Elem);
126 }
127 return this->size() < OrigSize;
128 }
129
130 /// Remove all elements for which Pred returns true.
131 /// Returns true if any elements were removed.
132 template <typename Pred> bool remove_if(Pred &&P) {
133 if (this->empty())
134 return false;
135
137 for (auto &Elem : *this)
138 if (P(Elem))
139 ToRemove.push_back(Elem);
140
141 for (auto &Elem : ToRemove)
142 this->erase(Elem);
143
144 return !ToRemove.empty();
145 }
146 };
147
148 class ContainerElementsMap : public DenseMap<ContainerId, ElementSet> {
150
151 public:
152 using DenseMap<ContainerId, ElementSet>::DenseMap;
153
154 /// Merge the elements of Other into this map. Returns true if any new
155 /// elements are added.
157 bool AssertNoElementsOverlap = false) {
158 bool Changed = false;
159 for (auto &[Container, Elements] : Other)
160 Changed |= (*this)[Container].merge(Elements, AssertNoElementsOverlap);
161 return Changed;
162 }
163
164 /// Remove all elements in Other from this map. Returns true if any
165 /// elements were removed.
167 bool Changed = false;
168 for (auto &[Container, Elements] : Other) {
169 assert(!Elements.empty() && "Stale row for Container in Other");
170 auto I = this->find(Container);
171 if (I == this->end())
172 continue;
173 Changed |= I->second.remove(Elements);
174 if (I->second.empty())
175 this->erase(Container);
176 }
177 return Changed;
178 }
179
180 /// Call V on each (Container, Elements) pair in this map.
181 ///
182 /// V should return true if it modifies any elements.
183 ///
184 /// Returns true if V returns true for any pair.
185 template <typename Visitor> bool visit(Visitor &&V) {
186 if (this->empty())
187 return false;
188
189 bool Changed = false;
191 for (auto &[Container, Elements] : *this) {
192 assert(!Elements.empty() && "empty row for container");
193 if (V(Container, Elements)) {
194 Changed = true;
195 if (Elements.empty())
196 ToRemove.push_back(Container);
197 }
198 }
199
200 for (auto &Container : ToRemove)
201 this->erase(Container);
202
203 return Changed;
204 }
205 };
206
207 class SuperNode;
208
209private:
210 using ElemToSuperNodeMap =
212
213 using SuperNodeDepsMap = DenseMap<SuperNode *, DenseSet<SuperNode *>>;
214
215public:
216 class SuperNode {
217 friend class WaitingOnGraph;
218 friend class WaitingOnGraphTest;
219
220 public:
222 : Defs(std::move(Defs)), Deps(std::move(Deps)) {}
223 ContainerElementsMap &defs() { return Defs; }
224 const ContainerElementsMap &defs() const { return Defs; }
225 ContainerElementsMap &deps() { return Deps; }
226 const ContainerElementsMap &deps() const { return Deps; }
227
228 private:
231
232 ElemToSuperNodeMap *RegisteredElemToSN = nullptr;
233
234 /// Add a mapping from the Defs in this SuperNode to SN (which may or may
235 /// not be the same as this).
236 void mapDefsTo(ElemToSuperNodeMap &ElemToSN, SuperNode *SN,
237 bool AbandonOldMapping = false) {
238 assert(!Defs.empty() && "Empty defs!?");
239 for (auto &[Container, Elements] : Defs) {
240 assert(!Elements.empty() && "Empty elements for container?");
241 auto &ContainerElemToSN = ElemToSN[Container];
242 for (auto &Elem : Elements)
243 ContainerElemToSN[Elem] = SN;
244 }
245 assert((AbandonOldMapping || !SN->RegisteredElemToSN ||
246 SN->RegisteredElemToSN == &ElemToSN) &&
247 "SN defs split across maps");
248 SN->RegisteredElemToSN = &ElemToSN;
249 }
250
251 /// Add a mapping from the Defs in this SuperNode to this.
252 /// (Equivalent to `SN.mapDefsTo(ElemToSN, &SN);`)
253 void mapDefsToThis(ElemToSuperNodeMap &ElemToSN,
254 bool AbandonOldMapping = false) {
255 mapDefsTo(ElemToSN, this, AbandonOldMapping);
256 }
257
258 /// Remove a mapping from the Defs in this SuperNode from the registered
259 /// ElemToSuperNodeMap. The mapping must already exist.
260 void unmapDefsFromThis() {
261 assert(RegisteredElemToSN && "No registered ElemToSuperNodeMap");
262 for (auto &[Container, Elements] : Defs) {
263 auto I = RegisteredElemToSN->find(Container);
264 assert(I != RegisteredElemToSN->end() && "Container not in map");
265 auto &ContainerElemToSN = I->second;
266 for (auto &Elem : Elements) {
267 assert(ContainerElemToSN[Elem] == this && "Mapping not present");
268 ContainerElemToSN.erase(Elem);
269 }
270 if (ContainerElemToSN.empty())
271 RegisteredElemToSN->erase(I);
272 }
273 RegisteredElemToSN = nullptr;
274 }
275
276 /// For all Defs of this node that are defined by some node in ElemToSN,
277 /// remove the Def from this map and add this SuperNode to the list of
278 /// dependants of the defining node.
279 ///
280 /// Returns true if SuperNodeDeps was changed.
281 bool hoistDeps(SuperNodeDepsMap &SuperNodeDeps,
282 ElemToSuperNodeMap &ElemToSN) {
283 return Deps.visit([&](ContainerId &Container, ElementSet &Elements) {
284 auto I = ElemToSN.find(Container);
285 if (I == ElemToSN.end())
286 return false;
287
288 auto &ContainerElemToSN = I->second;
289 return Elements.remove_if([&](const ElementId &Elem) {
290 auto J = ContainerElemToSN.find(Elem);
291 if (J == ContainerElemToSN.end())
292 return false;
293
294 auto *DefSN = J->second;
295 if (DefSN != this)
296 SuperNodeDeps[DefSN].insert(this);
297 return true;
298 });
299 });
300 }
301 };
302
303private:
304 /// Fast visit with removal.
305 ///
306 /// Visits the elements of Vec, removing each element for which V returns
307 /// true.
308 ///
309 /// This is O(1) in the number of elements removed, but does not preserve
310 /// element order.
311 template <typename Vector, typename Visitor>
312 static void visitWithRemoval(Vector &Vec, Visitor &&V) {
313 for (size_t I = 0; I != Vec.size();) {
314 if (V(Vec[I])) {
315 if (I != Vec.size() - 1)
316 std::swap(Vec[I], Vec.back());
317 Vec.pop_back();
318 } else
319 ++I;
320 }
321 }
322
323 class Coalescer {
324 public:
325 std::unique_ptr<SuperNode> addOrCreateSuperNode(ContainerElementsMap Defs,
326 ContainerElementsMap Deps) {
327 auto H = getHash(Deps);
328 if (auto *ExistingSN = findCanonicalSuperNode(H, Deps)) {
329 ExistingSN->Defs.merge(Defs, /* AssertNoElementsOverlap */ true);
330 return nullptr;
331 }
332
333 auto NewSN =
334 std::make_unique<SuperNode>(std::move(Defs), std::move(Deps));
335 CanonicalSNs[H].push_back(NewSN.get());
336 assert(!SNHashes.count(NewSN.get()));
337 SNHashes[NewSN.get()] = H;
338 return NewSN;
339 }
340
341 void coalesce(std::vector<std::unique_ptr<SuperNode>> &SNs,
342 ElemToSuperNodeMap &ElemToSN,
343 bool AbandonOldMapping = false) {
344 visitWithRemoval(SNs, [&](std::unique_ptr<SuperNode> &SN) {
345 assert(!SNHashes.count(SN.get()) &&
346 "Elements of SNs should be new to the coalescer");
347 auto H = getHash(SN->Deps);
348 if (auto *CanonicalSN = findCanonicalSuperNode(H, SN->Deps)) {
349 SN->mapDefsTo(ElemToSN, CanonicalSN, AbandonOldMapping);
350 CanonicalSN->Defs.merge(SN->Defs, /* AssertNoElementsOverlap */ true);
351 return true;
352 }
353 CanonicalSNs[H].push_back(SN.get());
354 SNHashes[SN.get()] = H;
355 return false;
356 });
357 }
358
359 /// Remove all coalescing information.
360 ///
361 /// This resets the Coalescer to the same functional state that it was
362 /// constructed in.
363 void clear() {
364 CanonicalSNs.clear();
365 SNHashes.clear();
366 }
367
368 /// Remove the given node from the Coalescer.
369 void erase(SuperNode *SN) {
370 hash_code H;
371
372 {
373 // Look up hash. We expect to find it in SNHashes.
374 auto I = SNHashes.find(SN);
375 assert(I != SNHashes.end() && "SN not tracked by coalescer");
376 H = I->second;
377 SNHashes.erase(I);
378 }
379
380 // Now remove from CanonicalSNs.
381 auto I = CanonicalSNs.find(H);
382 assert(I != CanonicalSNs.end() && "Hash not in CanonicalSNs");
383 auto &SNs = I->second;
384
385 size_t J = 0;
386 for (; J != SNs.size(); ++J)
387 if (SNs[J] == SN)
388 break;
389
390 assert(J < SNs.size() && "SN not in CanonicalSNs map");
391 std::swap(SNs[J], SNs.back());
392 SNs.pop_back();
393
394 if (SNs.empty())
395 CanonicalSNs.erase(I);
396 }
397
398 private:
399 hash_code getHash(const ContainerElementsMap &M) {
400 SmallVector<ContainerId> SortedContainers;
401 SortedContainers.reserve(M.size());
402 for (auto &[Container, Elems] : M)
403 SortedContainers.push_back(Container);
404 llvm::sort(SortedContainers);
405 hash_code Hash(0);
406 for (auto &Container : SortedContainers) {
407 auto &ContainerElems = M.at(Container);
408 SmallVector<ElementId> SortedElems(ContainerElems.begin(),
409 ContainerElems.end());
410 llvm::sort(SortedElems);
411 Hash = hash_combine(Hash, Container, hash_combine_range(SortedElems));
412 }
413 return Hash;
414 }
415
416 SuperNode *findCanonicalSuperNode(hash_code H,
417 const ContainerElementsMap &M) {
418 for (auto *SN : CanonicalSNs[H])
419 if (SN->Deps == M)
420 return SN;
421 return nullptr;
422 }
423
424 DenseMap<hash_code, SmallVector<SuperNode *>> CanonicalSNs;
425 DenseMap<SuperNode *, hash_code> SNHashes;
426 };
427
428public:
429 /// Build SuperNodes from (definition-set, dependence-set) pairs.
430 ///
431 /// Coalesces definition-sets with identical dependence-sets.
433 public:
435 if (Defs.empty())
436 return;
437 Deps.remove(Defs); // Remove any self-reference.
438 if (auto SN = C.addOrCreateSuperNode(std::move(Defs), std::move(Deps)))
439 SNs.push_back(std::move(SN));
440 }
441 std::vector<std::unique_ptr<SuperNode>> takeSuperNodes() {
442 C.clear();
443 return std::move(SNs);
444 }
445
446 private:
447 Coalescer C;
448 std::vector<std::unique_ptr<SuperNode>> SNs;
449 };
450
451 class SimplifyResult {
452 friend class WaitingOnGraph;
453 friend class WaitingOnGraphTest;
454
455 public:
456 const std::vector<std::unique_ptr<SuperNode>> &superNodes() const {
457 return SNs;
458 }
459
460 private:
461 SimplifyResult(std::vector<std::unique_ptr<SuperNode>> SNs,
462 ElemToSuperNodeMap ElemToSN)
463 : SNs(std::move(SNs)), ElemToSN(std::move(ElemToSN)) {}
464 std::vector<std::unique_ptr<SuperNode>> SNs;
465 ElemToSuperNodeMap ElemToSN;
466 };
467
469 public:
470 virtual ~OpRecorder() = default;
471 virtual void
472 recordSimplify(const std::vector<std::unique_ptr<SuperNode>> &SNs) = 0;
473 virtual void recordFail(const ContainerElementsMap &Failed) = 0;
474 virtual void recordEnd() = 0;
475 };
476
477 /// Preprocess a list of SuperNodes to remove all intra-SN dependencies.
478 static SimplifyResult simplify(std::vector<std::unique_ptr<SuperNode>> SNs,
479 OpRecorder *Rec = nullptr) {
480 if (Rec)
481 Rec->recordSimplify(SNs);
482
483 // Build ElemToSN map.
484 ElemToSuperNodeMap ElemToSN;
485 for (auto &SN : SNs)
486 SN->mapDefsToThis(ElemToSN);
487
488 SuperNodeDepsMap SuperNodeDeps;
489 hoistDeps(SNs, SuperNodeDeps, ElemToSN);
490 propagateDeps(SuperNodeDeps);
491
492 // Pre-coalesce nodes.
493 Coalescer().coalesce(SNs, ElemToSN);
494
495 return {std::move(SNs), std::move(ElemToSN)};
496 }
497
498 struct EmitResult {
499 std::vector<std::unique_ptr<SuperNode>> Ready;
500 std::vector<std::unique_ptr<SuperNode>> Failed;
501 };
502
503 enum class ExternalState { None, Ready, Failed };
504
505 /// Add the given SuperNodes to the graph, returning any SuperNodes that
506 /// move to the Ready or Failed states as a result.
507 /// The GetExternalState function is used to represent SuperNodes that have
508 /// already become Ready or Failed (since such nodes are not explicitly
509 /// represented in the graph).
510 template <typename GetExternalStateFn>
511 EmitResult emit(SimplifyResult SR, GetExternalStateFn &&GetExternalState) {
512 auto NewSNs = std::move(SR.SNs);
513 auto ElemToNewSN = std::move(SR.ElemToSN);
514
515 // First process any dependencies on nodes with external state.
516 auto FailedSNs = processExternalDeps(NewSNs, GetExternalState);
517
518 SuperNodeDepsMap SuperNodeDeps;
519
520 // Collect the PendingSNs whose dep sets are about to be modified.
521 std::vector<std::unique_ptr<SuperNode>> ModifiedPendingSNs;
522 visitWithRemoval(PendingSNs, [&](std::unique_ptr<SuperNode> &SN) {
523 if (SN->hoistDeps(SuperNodeDeps, ElemToNewSN)) {
524 ModifiedPendingSNs.push_back(std::move(SN));
525 return true;
526 }
527 return false;
528 });
529
530 // Remove SNs whose deps have been modified from the coalescer.
531 for (auto &SN : ModifiedPendingSNs)
532 CoalesceToPendingSNs.erase(SN.get());
533
534 hoistDeps(NewSNs, SuperNodeDeps, ElemToPendingSN);
535 propagateDeps(SuperNodeDeps);
536
537 propagateFailures(FailedSNs, SuperNodeDeps);
538
539 // Process supernodes. Pending first, since we'll update PendingSNs when we
540 // incorporate NewSNs.
541 std::vector<std::unique_ptr<SuperNode>> ReadyNodes, FailedNodes;
542 processReadyOrFailed(ModifiedPendingSNs, ReadyNodes, FailedNodes,
543 SuperNodeDeps, FailedSNs, true);
544 processReadyOrFailed(NewSNs, ReadyNodes, FailedNodes, SuperNodeDeps,
545 FailedSNs, false);
546
547 CoalesceToPendingSNs.coalesce(ModifiedPendingSNs, ElemToPendingSN);
548 CoalesceToPendingSNs.coalesce(NewSNs, ElemToPendingSN,
549 /* AbandonOldMapping = */ true);
550
551 // Integrate remaining ModifiedPendingSNs and NewSNs into PendingSNs.
552 for (auto &SN : ModifiedPendingSNs)
553 PendingSNs.push_back(std::move(SN));
554
555 // Update ElemToPendingSN for the remaining elements.
556 for (auto &SN : NewSNs) {
557 SN->mapDefsToThis(ElemToPendingSN, /* AbandonOldMapping = */ true);
558 PendingSNs.push_back(std::move(SN));
559 }
560
561 return {std::move(ReadyNodes), std::move(FailedNodes)};
562 }
563
564 /// Identify the given symbols as Failed.
565 /// The elements of the Failed map will not be included in the returned
566 /// result, so clients should take whatever actions are needed to mark
567 /// this as failed in their external representation.
568 std::vector<std::unique_ptr<SuperNode>>
569 fail(const ContainerElementsMap &Failed, OpRecorder *Rec = nullptr) {
570 if (Rec)
571 Rec->recordFail(Failed);
572
573 std::vector<std::unique_ptr<SuperNode>> FailedSNs;
574
575 visitWithRemoval(PendingSNs, [&](std::unique_ptr<SuperNode> &SN) {
576 for (auto &[Container, Elements] : SN->Deps) {
577 auto I = Failed.find(Container);
578 if (I == Failed.end())
579 continue;
580
581 auto &FailedElems = I->second;
582 for (auto &Elem : Elements) {
583 if (FailedElems.count(Elem)) {
584 CoalesceToPendingSNs.erase(SN.get());
585 SN->unmapDefsFromThis();
586 FailedSNs.push_back(std::move(SN));
587 return true;
588 }
589 }
590 }
591 return false;
592 });
593
594 return FailedSNs;
595 }
596
598 bool AllGood = true;
599 auto ErrLog = [&]() -> raw_ostream & {
600 AllGood = false;
601 return Log;
602 };
603
604 size_t DefCount = 0;
605 for (auto &PendingSN : PendingSNs) {
606 if (PendingSN->Deps.empty())
607 ErrLog() << "Pending SN " << PendingSN.get() << " has empty dep set.\n";
608 else {
609 bool BadElem = false;
610 for (auto &[Container, Elems] : PendingSN->Deps) {
611 auto I = ElemToPendingSN.find(Container);
612 if (I == ElemToPendingSN.end())
613 continue;
614 if (Elems.empty())
615 ErrLog() << "Pending SN " << PendingSN.get()
616 << " has dependence map entry for " << Container
617 << " with empty element set.\n";
618 for (auto &Elem : Elems) {
619 if (I->second.count(Elem)) {
620 ErrLog() << "Pending SN " << PendingSN.get()
621 << " has dependence on emitted element ( " << Container
622 << ", " << Elem << ")\n";
623 BadElem = true;
624 break;
625 }
626 }
627 if (BadElem)
628 break;
629 }
630 }
631
632 for (auto &[Container, Elems] : PendingSN->Defs) {
633 if (Elems.empty())
634 ErrLog() << "Pending SN " << PendingSN.get()
635 << " has def map entry for " << Container
636 << " with empty element set.\n";
637 DefCount += Elems.size();
638 auto I = ElemToPendingSN.find(Container);
639 if (I == ElemToPendingSN.end())
640 ErrLog() << "Pending SN " << PendingSN.get() << " has "
641 << Elems.size() << " defs in container " << Container
642 << " not covered by ElemsToPendingSN.\n";
643 else {
644 for (auto &Elem : Elems) {
645 auto J = I->second.find(Elem);
646 if (J == I->second.end())
647 ErrLog() << "Pending SN " << PendingSN.get() << " has element ("
648 << Container << ", " << Elem
649 << ") not covered by ElemsToPendingSN.\n";
650 else if (J->second != PendingSN.get())
651 ErrLog() << "ElemToPendingSN value invalid for (" << Container
652 << ", " << Elem << ")\n";
653 }
654 }
655 }
656 }
657
658 size_t DefCount2 = 0;
659 for (auto &[Container, Elems] : ElemToPendingSN)
660 DefCount2 += Elems.size();
661
662 assert(DefCount2 >= DefCount);
663 if (DefCount2 != DefCount)
664 ErrLog() << "ElemToPendingSN contains extra elements.\n";
665
666 return AllGood;
667 }
668
669private:
670 // Replace individual dependencies with supernode dependencies.
671 static void hoistDeps(std::vector<std::unique_ptr<SuperNode>> &SNs,
672 SuperNodeDepsMap &SuperNodeDeps,
673 ElemToSuperNodeMap &ElemToSN) {
674 // For all SNs...
675 for (auto &SN : SNs)
676 SN->hoistDeps(SuperNodeDeps, ElemToSN);
677 }
678
679 // Compute transitive closure of deps for each node.
680 static void propagateDeps(SuperNodeDepsMap &SuperNodeDeps) {
681
682 // Early exit for self-contained emits.
683 if (SuperNodeDeps.empty())
684 return;
685
687 Worklist.reserve(SuperNodeDeps.size());
688 for (auto &[SN, SNDependants] : SuperNodeDeps)
689 Worklist.push_back(SN);
690
691 while (true) {
692 DenseSet<SuperNode *> ToVisitNext;
693
694 // TODO: See if topo-sorting worklist improves convergence.
695
696 while (!Worklist.empty()) {
697 auto *SN = Worklist.pop_back_val();
698 auto I = SuperNodeDeps.find(SN);
699 if (I == SuperNodeDeps.end())
700 continue;
701
702 for (auto *DependantSN : I->second)
703 if (DependantSN->Deps.merge(SN->Deps))
704 ToVisitNext.insert(DependantSN);
705 }
706
707 if (ToVisitNext.empty())
708 break;
709
710 Worklist.append(ToVisitNext.begin(), ToVisitNext.end());
711 }
712 }
713
714 static void propagateFailures(DenseSet<SuperNode *> &FailedNodes,
715 SuperNodeDepsMap &SuperNodeDeps) {
716 if (FailedNodes.empty())
717 return;
718
719 SmallVector<SuperNode *> Worklist(FailedNodes.begin(), FailedNodes.end());
720
721 while (!Worklist.empty()) {
722 auto *SN = Worklist.pop_back_val();
723 auto I = SuperNodeDeps.find(SN);
724 if (I == SuperNodeDeps.end())
725 continue;
726
727 for (auto *DependantSN : I->second)
728 if (FailedNodes.insert(DependantSN).second)
729 Worklist.push_back(DependantSN);
730 }
731 }
732
733 template <typename GetExternalStateFn>
734 static DenseSet<SuperNode *>
735 processExternalDeps(std::vector<std::unique_ptr<SuperNode>> &SNs,
736 GetExternalStateFn &GetExternalState) {
737 DenseSet<SuperNode *> FailedSNs;
738 for (auto &SN : SNs)
739 SN->Deps.visit([&](ContainerId &Container, ElementSet &Elements) {
740 return Elements.remove_if([&](ElementId &Elem) {
741 switch (GetExternalState(Container, Elem)) {
743 return false;
745 return true;
747 FailedSNs.insert(SN.get());
748 return true;
749 };
750 llvm_unreachable("Unknown ExternalState enum");
751 });
752 });
753
754 return FailedSNs;
755 }
756
757 void processReadyOrFailed(std::vector<std::unique_ptr<SuperNode>> &SNs,
758 std::vector<std::unique_ptr<SuperNode>> &Ready,
759 std::vector<std::unique_ptr<SuperNode>> &Failed,
760 SuperNodeDepsMap &SuperNodeDeps,
761 const DenseSet<SuperNode *> &FailedSNs,
762 bool UnmapFromElemToSN) {
763
764 visitWithRemoval(SNs, [&](std::unique_ptr<SuperNode> &SN) {
765 bool SNFailed = FailedSNs.count(SN.get());
766 bool SNReady = SN->Deps.empty();
767
768 if (SNReady || SNFailed) {
769 if (UnmapFromElemToSN)
770 SN->unmapDefsFromThis();
771 auto &ToList = SNFailed ? Failed : Ready;
772 ToList.push_back(std::move(SN));
773 return true;
774 }
775 return false;
776 });
777 }
778
779 std::vector<std::unique_ptr<SuperNode>> PendingSNs;
780 ElemToSuperNodeMap ElemToPendingSN;
781 Coalescer CoalesceToPendingSNs;
782};
783
784} // namespace llvm::orc::detail
785
786#endif // LLVM_EXECUTIONENGINE_ORC_WAITINGONGRAPH_H
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
ReachingDefInfo InstSet & ToRemove
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
#define P(N)
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.
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
void reserve(size_type N)
void append(ItTy in_start, ItTy in_end)
Add the specified range to the end of the SmallVector.
void push_back(const T &Elt)
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
bool remove(const ContainerElementsMap &Other)
Remove all elements in Other from this map.
bool merge(const ContainerElementsMap &Other, bool AssertNoElementsOverlap=false)
Merge the elements of Other into this map.
bool visit(Visitor &&V)
Call V on each (Container, Elements) pair in this map.
bool remove_if(Pred &&P)
Remove all elements for which Pred returns true.
bool remove(const ElementSet &Other)
Remove all elements in Other from this set.
bool merge(const ElementSet &Other, bool AssertNoOverlap=false)
Merge the elements of Other into this set.
virtual void recordFail(const ContainerElementsMap &Failed)=0
virtual void recordSimplify(const std::vector< std::unique_ptr< SuperNode > > &SNs)=0
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, OpRecorder *Rec=nullptr)
Identify the given symbols as Failed.
static SimplifyResult simplify(std::vector< std::unique_ptr< SuperNode > > SNs, OpRecorder *Rec=nullptr)
Preprocess a list of SuperNodes to remove all intra-SN dependencies.
EmitResult emit(SimplifyResult SR, GetExternalStateFn &&GetExternalState)
Add the given SuperNodes to the graph, returning any SuperNodes that move to the Ready or Failed stat...
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
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ C
The default llvm calling convention, compatible with C.
Definition CallingConv.h:34
auto find(R &&Range, const T &Val)
Provide wrappers to std::find which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1765
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
testing::Matcher< const detail::ErrorHolder & > Failed()
Definition Error.h:198
void erase(Container &C, ValueType V)
Wrapper function to remove a value from a container:
Definition STLExtras.h:2200
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...
@ Other
Any other memory.
Definition ModRef.h:68
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