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/Hashing.h"
18#include "llvm/ADT/STLExtras.h"
21
22#include <algorithm>
23#include <vector>
24
25namespace llvm::orc::detail {
26
27class WaitingOnGraphTest;
28
29/// WaitingOnGraph class template.
30///
31/// This type is intended to provide efficient dependence tracking for Symbols
32/// in an ORC program.
33///
34/// WaitingOnGraph models a directed graph with four partitions:
35/// 1. Not-yet-emitted nodes: Nodes identified as waited-on in an emit
36/// operation.
37/// 2. Emitted nodes: Nodes emitted and waiting on some non-empty set of
38/// other nodes.
39/// 3. Ready nodes: Nodes emitted and not waiting on any other nodes
40/// (either because they weren't waiting on any nodes when they were
41/// emitted, or because all transitively waited-on nodes have since
42/// been emitted).
43/// 4. Failed nodes: Nodes that have been marked as failed-to-emit, and
44/// nodes that were found to transitively wait-on some failed node.
45///
46/// Nodes are added to the graph by *emit* and *fail* operations.
47///
48/// The *emit* operation takes a bipartite *local dependence graph* as an
49/// argument and returns...
50/// a. the set of nodes (both existing and newly added from the local
51/// dependence graph) whose waiting-on set is the empty set, and...
52/// b. the set of newly added nodes that are found to depend on failed
53/// nodes.
54///
55/// The *fail* operation takes a set of failed nodes and returns the set of
56/// Emitted nodes that were waiting on the failed nodes.
57///
58/// The concrete representation adopts several approaches for efficiency:
59///
60/// 1. Only *Emitted* and *Not-yet-emitted* nodes are represented explicitly.
61/// *Ready* and *Failed* nodes are represented by the values returned by the
62/// GetExternalStateFn argument to *emit*.
63///
64/// 2. Labels are (*Container*, *Element*) pairs that are intended to represent
65/// ORC symbols (ORC uses types Container = JITDylib,
66/// Element = NonOwningSymbolStringPtr). The internal representation of the
67/// graph is optimized on the assumption that there are many more Elements
68/// (symbol names) than Containers (JITDylibs) used to construct the labels.
69/// (Consider for example the common case where most JIT'd code is placed in
70/// a single "main" JITDylib).
71///
72/// 3. The data structure stores *SuperNodes* which have multiple labels. This
73/// reduces the number of nodes and edges in the graph in the common case
74/// where many JIT symbols have the same set of dependencies. SuperNodes are
75/// coalesced when their dependence sets become equal.
76///
77/// 4. The *simplify* method can be applied to an initial *local dependence
78/// graph* (as a list of SuperNodes) to eliminate any internal dependence
79/// relationships that would have to be propagated internally by *emit*.
80/// Access to the WaitingOnGraph is assumed to be guarded by a mutex (ORC
81/// will access it from multiple threads) so this allows some pre-processing
82/// to be performed outside the mutex.
83template <typename ContainerIdT, typename ElementIdT> class WaitingOnGraph {
84 friend class WaitingOnGraphTest;
85
86public:
87 using ContainerId = ContainerIdT;
88 using ElementId = ElementIdT;
89
90 class ElementSet : public DenseSet<ElementId> {
91 friend class ElementSetTest;
92
93 public:
95
96 /// Merge the elements of Other into this set. Returns true if any new
97 /// elements are added.
98 bool merge(const ElementSet &Other, bool AssertNoOverlap = false) {
99 size_t OrigSize = this->size();
100 this->insert(Other.begin(), Other.end());
101 assert((!AssertNoOverlap || this->size() == (OrigSize + Other.size())) &&
102 "merge of overlapping elements");
103 return this->size() != OrigSize;
104 }
105
106 /// Remove all elements in Other from this set. Returns true if any
107 /// elements were removed.
108 bool remove(const ElementSet &Other) {
109 size_t OrigSize = this->size();
110
111 // Early out for empty sets.
112 if (OrigSize == 0 || Other.empty())
113 return false;
114
115 // TODO: Tweak condition to account for SmallVector cost. We may want to
116 // prefer iterating over elements if the size difference is small.
117 if (OrigSize > Other.size()) {
118 for (auto &Elem : Other)
119 this->erase(Elem);
120 } else {
122 for (auto &Elem : *this)
123 if (Other.count(Elem))
124 ToRemove.push_back(Elem);
125 for (auto &Elem : ToRemove)
126 this->erase(Elem);
127 }
128 return this->size() < OrigSize;
129 }
130
131 /// Remove all elements for which Pred returns true.
132 /// Returns true if any elements were removed.
133 template <typename Pred> bool remove_if(Pred &&P) {
134 if (this->empty())
135 return false;
136
138 for (auto &Elem : *this)
139 if (P(Elem))
140 ToRemove.push_back(Elem);
141
142 for (auto &Elem : ToRemove)
143 this->erase(Elem);
144
145 return !ToRemove.empty();
146 }
147 };
148
149 class ContainerElementsMap : public DenseMap<ContainerId, ElementSet> {
151
152 public:
153 using DenseMap<ContainerId, ElementSet>::DenseMap;
154
155 /// Merge the elements of Other into this map. Returns true if any new
156 /// elements are added.
158 bool AssertNoElementsOverlap = false) {
159 bool Changed = false;
160 for (auto &[Container, Elements] : Other)
161 Changed |= (*this)[Container].merge(Elements, AssertNoElementsOverlap);
162 return Changed;
163 }
164
165 /// Remove all elements in Other from this map. Returns true if any
166 /// elements were removed.
168 bool Changed = false;
169 for (auto &[Container, Elements] : Other) {
170 assert(!Elements.empty() && "Stale row for Container in Other");
171 auto I = this->find(Container);
172 if (I == this->end())
173 continue;
174 Changed |= I->second.remove(Elements);
175 if (I->second.empty())
176 this->erase(Container);
177 }
178 return Changed;
179 }
180
181 /// Call V on each (Container, Elements) pair in this map.
182 ///
183 /// V should return true if it modifies any elements.
184 ///
185 /// Returns true if V returns true for any pair.
186 template <typename Visitor> bool visit(Visitor &&V) {
187 if (this->empty())
188 return false;
189
190 bool Changed = false;
192 for (auto &[Container, Elements] : *this) {
193 assert(!Elements.empty() && "empty row for container");
194 if (V(Container, Elements)) {
195 Changed = true;
196 if (Elements.empty())
197 ToRemove.push_back(Container);
198 }
199 }
200
201 for (auto &Container : ToRemove)
202 this->erase(Container);
203
204 return Changed;
205 }
206 };
207
208 class SuperNode;
209
210private:
211 using ElemToSuperNodeMap =
213
214 using SuperNodeDepsMap = DenseMap<SuperNode *, DenseSet<SuperNode *>>;
215
216public:
217 class SuperNode {
218 friend class WaitingOnGraph;
219 friend class WaitingOnGraphTest;
220
221 public:
223 : Defs(std::move(Defs)), Deps(std::move(Deps)) {}
224 ContainerElementsMap &defs() { return Defs; }
225 const ContainerElementsMap &defs() const { return Defs; }
226 ContainerElementsMap &deps() { return Deps; }
227 const ContainerElementsMap &deps() const { return Deps; }
228
229 private:
232
233 ElemToSuperNodeMap *RegisteredElemToSN = nullptr;
234
235 /// Add a mapping from the Defs in this SuperNode to SN (which may or may
236 /// not be the same as this).
237 void mapDefsTo(ElemToSuperNodeMap &ElemToSN, SuperNode *SN,
238 bool AbandonOldMapping = false) {
239 assert(!Defs.empty() && "Empty defs!?");
240 for (auto &[Container, Elements] : Defs) {
241 assert(!Elements.empty() && "Empty elements for container?");
242 auto &ContainerElemToSN = ElemToSN[Container];
243 for (auto &Elem : Elements)
244 ContainerElemToSN[Elem] = SN;
245 }
246 assert((AbandonOldMapping || !SN->RegisteredElemToSN ||
247 SN->RegisteredElemToSN == &ElemToSN) &&
248 "SN defs split across maps");
249 SN->RegisteredElemToSN = &ElemToSN;
250 }
251
252 /// Add a mapping from the Defs in this SuperNode to this.
253 /// (Equivalent to `SN.mapDefsTo(ElemToSN, &SN);`)
254 void mapDefsToThis(ElemToSuperNodeMap &ElemToSN,
255 bool AbandonOldMapping = false) {
256 mapDefsTo(ElemToSN, this, AbandonOldMapping);
257 }
258
259 /// Remove a mapping from the Defs in this SuperNode from the registered
260 /// ElemToSuperNodeMap. The mapping must already exist.
261 void unmapDefsFromThis() {
262 assert(RegisteredElemToSN && "No registered ElemToSuperNodeMap");
263 for (auto &[Container, Elements] : Defs) {
264 auto I = RegisteredElemToSN->find(Container);
265 assert(I != RegisteredElemToSN->end() && "Container not in map");
266 auto &ContainerElemToSN = I->second;
267 for (auto &Elem : Elements) {
268 assert(ContainerElemToSN[Elem] == this && "Mapping not present");
269 ContainerElemToSN.erase(Elem);
270 }
271 if (ContainerElemToSN.empty())
272 RegisteredElemToSN->erase(I);
273 }
274 RegisteredElemToSN = nullptr;
275 }
276
277 /// For all Defs of this node that are defined by some node in ElemToSN,
278 /// remove the Def from this map and add this SuperNode to the list of
279 /// dependants of the defining node.
280 ///
281 /// Returns true if any elements were removed.
282 bool hoistDeps(SuperNodeDepsMap &SuperNodeDeps,
283 ElemToSuperNodeMap &ElemToSN) {
284 return Deps.visit([&](ContainerId &Container, ElementSet &Elements) {
285 auto I = ElemToSN.find(Container);
286 if (I == ElemToSN.end())
287 return false;
288
289 auto &ContainerElemToSN = I->second;
290 return Elements.remove_if([&](const ElementId &Elem) {
291 auto J = ContainerElemToSN.find(Elem);
292 if (J == ContainerElemToSN.end())
293 return false;
294
295 auto *DefSN = J->second;
296 if (DefSN != this)
297 SuperNodeDeps[DefSN].insert(this);
298 return true;
299 });
300 });
301 }
302 };
303
304private:
305 /// Fast visit with removal.
306 ///
307 /// Visits the elements of Vec, removing each element for which V returns
308 /// true.
309 ///
310 /// This is O(1) in the number of elements removed, but does not preserve
311 /// element order.
312 template <typename Vector, typename Visitor>
313 static void visitWithRemoval(Vector &Vec, Visitor &&V) {
314 for (size_t I = 0; I != Vec.size();) {
315 if (V(Vec[I])) {
316 if (I != Vec.size() - 1)
317 std::swap(Vec[I], Vec.back());
318 Vec.pop_back();
319 } else
320 ++I;
321 }
322 }
323
324 class Coalescer {
325 public:
326 std::unique_ptr<SuperNode> addOrCreateSuperNode(ContainerElementsMap Defs,
327 ContainerElementsMap Deps) {
328 auto H = getHash(Deps);
329 if (auto *ExistingSN = findCanonicalSuperNode(H, Deps)) {
330 ExistingSN->Defs.merge(Defs, /* AssertNoElementsOverlap */ true);
331 return nullptr;
332 }
333
334 auto NewSN =
335 std::make_unique<SuperNode>(std::move(Defs), std::move(Deps));
336 CanonicalSNs[H].push_back(NewSN.get());
337 assert(!SNHashes.count(NewSN.get()));
338 SNHashes[NewSN.get()] = H;
339 return NewSN;
340 }
341
342 void coalesce(std::vector<std::unique_ptr<SuperNode>> &SNs,
343 ElemToSuperNodeMap &ElemToSN,
344 bool AbandonOldMapping = false) {
345 visitWithRemoval(SNs, [&](std::unique_ptr<SuperNode> &SN) {
346 assert(!SNHashes.count(SN.get()) &&
347 "Elements of SNs should be new to the coalescer");
348 auto H = getHash(SN->Deps);
349 if (auto *CanonicalSN = findCanonicalSuperNode(H, SN->Deps)) {
350 SN->mapDefsTo(ElemToSN, CanonicalSN, AbandonOldMapping);
351 CanonicalSN->Defs.merge(SN->Defs, /* AssertNoElementsOverlap */ true);
352 return true;
353 }
354 CanonicalSNs[H].push_back(SN.get());
355 SNHashes[SN.get()] = H;
356 return false;
357 });
358 }
359
360 /// Remove all coalescing information.
361 ///
362 /// This resets the Coalescer to the same functional state that it was
363 /// constructed in.
364 void clear() {
365 CanonicalSNs.clear();
366 SNHashes.clear();
367 }
368
369 /// Remove the given node from the Coalescer.
370 void erase(SuperNode *SN) {
371 hash_code H;
372
373 {
374 // Look up hash. We expect to find it in SNHashes.
375 auto I = SNHashes.find(SN);
376 assert(I != SNHashes.end() && "SN not tracked by coalescer");
377 H = I->second;
378 SNHashes.erase(I);
379 }
380
381 // Now remove from CanonicalSNs.
382 auto I = CanonicalSNs.find(H);
383 assert(I != CanonicalSNs.end() && "Hash not in CanonicalSNs");
384 auto &SNs = I->second;
385
386 size_t J = 0;
387 for (; J != SNs.size(); ++J)
388 if (SNs[J] == SN)
389 break;
390
391 assert(J < SNs.size() && "SN not in CanonicalSNs map");
392 std::swap(SNs[J], SNs.back());
393 SNs.pop_back();
394
395 if (SNs.empty())
396 CanonicalSNs.erase(I);
397 }
398
399 private:
400 hash_code getHash(const ContainerElementsMap &M) {
401 SmallVector<ContainerId> SortedContainers;
402 SortedContainers.reserve(M.size());
403 for (auto &[Container, Elems] : M)
404 SortedContainers.push_back(Container);
405 llvm::sort(SortedContainers);
406 hash_code Hash(0);
407 for (auto &Container : SortedContainers) {
408 auto &ContainerElems = M.at(Container);
409 SmallVector<ElementId> SortedElems(ContainerElems.begin(),
410 ContainerElems.end());
411 llvm::sort(SortedElems);
412 Hash = hash_combine(Hash, Container, hash_combine_range(SortedElems));
413 }
414 return Hash;
415 }
416
417 SuperNode *findCanonicalSuperNode(hash_code H,
418 const ContainerElementsMap &M) {
419 for (auto *SN : CanonicalSNs[H])
420 if (SN->Deps == M)
421 return SN;
422 return nullptr;
423 }
424
425 DenseMap<hash_code, SmallVector<SuperNode *>> CanonicalSNs;
426 DenseMap<SuperNode *, hash_code> SNHashes;
427 };
428
429public:
430 /// Build SuperNodes from (definition-set, dependence-set) pairs.
431 ///
432 /// Coalesces definition-sets with identical dependence-sets.
434 public:
436 if (Defs.empty())
437 return;
438 Deps.remove(Defs); // Remove any self-reference.
439 if (auto SN = C.addOrCreateSuperNode(std::move(Defs), std::move(Deps)))
440 SNs.push_back(std::move(SN));
441 }
442 std::vector<std::unique_ptr<SuperNode>> takeSuperNodes() {
443 C.clear();
444 return std::move(SNs);
445 }
446
447 private:
448 Coalescer C;
449 std::vector<std::unique_ptr<SuperNode>> SNs;
450 };
451
452 class SimplifyResult {
453 friend class WaitingOnGraph;
454 friend class WaitingOnGraphTest;
455
456 public:
457 const std::vector<std::unique_ptr<SuperNode>> &superNodes() const {
458 return SNs;
459 }
460
461 private:
462 SimplifyResult(std::vector<std::unique_ptr<SuperNode>> SNs,
463 ElemToSuperNodeMap ElemToSN)
464 : SNs(std::move(SNs)), ElemToSN(std::move(ElemToSN)) {}
465 std::vector<std::unique_ptr<SuperNode>> SNs;
466 ElemToSuperNodeMap ElemToSN;
467 };
468
470 public:
471 virtual ~OpRecorder() = default;
472 virtual void
473 recordSimplify(const std::vector<std::unique_ptr<SuperNode>> &SNs) = 0;
474 virtual void recordFail(const ContainerElementsMap &Failed) = 0;
475 virtual void recordEnd() = 0;
476 };
477
478 /// Preprocess a list of SuperNodes to remove all intra-SN dependencies.
479 static SimplifyResult simplify(std::vector<std::unique_ptr<SuperNode>> SNs,
480 OpRecorder *Rec = nullptr) {
481 if (Rec)
482 Rec->recordSimplify(SNs);
483
484 // Build ElemToSN map.
485 ElemToSuperNodeMap ElemToSN;
486 for (auto &SN : SNs)
487 SN->mapDefsToThis(ElemToSN);
488
489 SuperNodeDepsMap SuperNodeDeps;
490 hoistDeps(SNs, SuperNodeDeps, ElemToSN);
491 propagateDeps(SuperNodeDeps);
492
493 // Pre-coalesce nodes.
494 Coalescer().coalesce(SNs, ElemToSN);
495
496 return {std::move(SNs), std::move(ElemToSN)};
497 }
498
499 struct EmitResult {
500 std::vector<std::unique_ptr<SuperNode>> Ready;
501 std::vector<std::unique_ptr<SuperNode>> Failed;
502 };
503
504 enum class ExternalState { None, Ready, Failed };
505
506 /// Add the given SuperNodes to the graph, returning any SuperNodes that
507 /// move to the Ready or Failed states as a result.
508 /// The GetExternalState function is used to represent SuperNodes that have
509 /// already become Ready or Failed (since such nodes are not explicitly
510 /// represented in the graph).
511 template <typename GetExternalStateFn>
512 EmitResult emit(SimplifyResult SR, GetExternalStateFn &&GetExternalState) {
513 auto NewSNs = std::move(SR.SNs);
514 auto ElemToNewSN = std::move(SR.ElemToSN);
515
516 // First process any dependencies on nodes with external state.
517 auto FailedSNs = processExternalDeps(NewSNs, GetExternalState);
518
519 SuperNodeDepsMap SuperNodeDeps;
520
521 // Collect the PendingSNs whose dep sets are about to be modified.
522 std::vector<std::unique_ptr<SuperNode>> ModifiedPendingSNs;
523 visitWithRemoval(PendingSNs, [&](std::unique_ptr<SuperNode> &SN) {
524 if (SN->hoistDeps(SuperNodeDeps, ElemToNewSN)) {
525 ModifiedPendingSNs.push_back(std::move(SN));
526 return true;
527 }
528 return false;
529 });
530
531 // Remove SNs whose deps have been modified from the coalescer.
532 for (auto &SN : ModifiedPendingSNs)
533 CoalesceToPendingSNs.erase(SN.get());
534
535 hoistDeps(NewSNs, SuperNodeDeps, ElemToPendingSN);
536 propagateDeps(SuperNodeDeps);
537
538 propagateFailures(FailedSNs, SuperNodeDeps);
539
540 // Process supernodes. Pending first, since we'll update PendingSNs when we
541 // incorporate NewSNs.
542 std::vector<std::unique_ptr<SuperNode>> ReadyNodes, FailedNodes;
543 processReadyOrFailed(ModifiedPendingSNs, ReadyNodes, FailedNodes,
544 SuperNodeDeps, FailedSNs, true);
545 processReadyOrFailed(NewSNs, ReadyNodes, FailedNodes, SuperNodeDeps,
546 FailedSNs, false);
547
548 CoalesceToPendingSNs.coalesce(ModifiedPendingSNs, ElemToPendingSN);
549 CoalesceToPendingSNs.coalesce(NewSNs, ElemToPendingSN,
550 /* AbandonOldMapping = */ true);
551
552 // Integrate remaining ModifiedPendingSNs and NewSNs into PendingSNs.
553 for (auto &SN : ModifiedPendingSNs)
554 PendingSNs.push_back(std::move(SN));
555
556 // Update ElemToPendingSN for the remaining elements.
557 for (auto &SN : NewSNs) {
558 SN->mapDefsToThis(ElemToPendingSN, /* AbandonOldMapping = */ true);
559 PendingSNs.push_back(std::move(SN));
560 }
561
562 return {std::move(ReadyNodes), std::move(FailedNodes)};
563 }
564
565 /// Identify the given symbols as Failed.
566 /// The elements of the Failed map will not be included in the returned
567 /// result, so clients should take whatever actions are needed to mark
568 /// this as failed in their external representation.
569 std::vector<std::unique_ptr<SuperNode>>
570 fail(const ContainerElementsMap &Failed, OpRecorder *Rec = nullptr) {
571 if (Rec)
572 Rec->recordFail(Failed);
573
574 std::vector<std::unique_ptr<SuperNode>> FailedSNs;
575
576 visitWithRemoval(PendingSNs, [&](std::unique_ptr<SuperNode> &SN) {
577 for (auto &[Container, Elements] : SN->Deps) {
578 auto I = Failed.find(Container);
579 if (I == Failed.end())
580 continue;
581
582 auto &FailedElems = I->second;
583 for (auto &Elem : Elements) {
584 if (FailedElems.count(Elem)) {
585 CoalesceToPendingSNs.erase(SN.get());
586 SN->unmapDefsFromThis();
587 FailedSNs.push_back(std::move(SN));
588 return true;
589 }
590 }
591 }
592 return false;
593 });
594
595 return FailedSNs;
596 }
597
599 bool AllGood = true;
600 auto ErrLog = [&]() -> raw_ostream & {
601 AllGood = false;
602 return Log;
603 };
604
605 size_t DefCount = 0;
606 for (auto &PendingSN : PendingSNs) {
607 if (PendingSN->Deps.empty())
608 ErrLog() << "Pending SN " << PendingSN.get() << " has empty dep set.\n";
609 else {
610 bool BadElem = false;
611 for (auto &[Container, Elems] : PendingSN->Deps) {
612 auto I = ElemToPendingSN.find(Container);
613 if (I == ElemToPendingSN.end())
614 continue;
615 if (Elems.empty())
616 ErrLog() << "Pending SN " << PendingSN.get()
617 << " has dependence map entry for " << Container
618 << " with empty element set.\n";
619 for (auto &Elem : Elems) {
620 if (I->second.count(Elem)) {
621 ErrLog() << "Pending SN " << PendingSN.get()
622 << " has dependence on emitted element ( " << Container
623 << ", " << Elem << ")\n";
624 BadElem = true;
625 break;
626 }
627 }
628 if (BadElem)
629 break;
630 }
631 }
632
633 for (auto &[Container, Elems] : PendingSN->Defs) {
634 if (Elems.empty())
635 ErrLog() << "Pending SN " << PendingSN.get()
636 << " has def map entry for " << Container
637 << " with empty element set.\n";
638 DefCount += Elems.size();
639 auto I = ElemToPendingSN.find(Container);
640 if (I == ElemToPendingSN.end())
641 ErrLog() << "Pending SN " << PendingSN.get() << " has "
642 << Elems.size() << " defs in container " << Container
643 << " not covered by ElemsToPendingSN.\n";
644 else {
645 for (auto &Elem : Elems) {
646 auto J = I->second.find(Elem);
647 if (J == I->second.end())
648 ErrLog() << "Pending SN " << PendingSN.get() << " has element ("
649 << Container << ", " << Elem
650 << ") not covered by ElemsToPendingSN.\n";
651 else if (J->second != PendingSN.get())
652 ErrLog() << "ElemToPendingSN value invalid for (" << Container
653 << ", " << Elem << ")\n";
654 }
655 }
656 }
657 }
658
659 size_t DefCount2 = 0;
660 for (auto &[Container, Elems] : ElemToPendingSN)
661 DefCount2 += Elems.size();
662
663 assert(DefCount2 >= DefCount);
664 if (DefCount2 != DefCount)
665 ErrLog() << "ElemToPendingSN contains extra elements.\n";
666
667 return AllGood;
668 }
669
670private:
671 // Replace individual dependencies with supernode dependencies.
672 static void hoistDeps(std::vector<std::unique_ptr<SuperNode>> &SNs,
673 SuperNodeDepsMap &SuperNodeDeps,
674 ElemToSuperNodeMap &ElemToSN) {
675 // For all SNs...
676 for (auto &SN : SNs)
677 SN->hoistDeps(SuperNodeDeps, ElemToSN);
678 }
679
680 // Compute transitive closure of deps for each node.
681 static void propagateDeps(SuperNodeDepsMap &SuperNodeDeps) {
682
683 // Early exit for self-contained emits.
684 if (SuperNodeDeps.empty())
685 return;
686
688 Worklist.reserve(SuperNodeDeps.size());
689 for (auto &[SN, SNDependants] : SuperNodeDeps)
690 Worklist.push_back(SN);
691
692 while (true) {
693 DenseSet<SuperNode *> ToVisitNext;
694
695 // TODO: See if topo-sorting worklist improves convergence.
696
697 while (!Worklist.empty()) {
698 auto *SN = Worklist.pop_back_val();
699 auto I = SuperNodeDeps.find(SN);
700 if (I == SuperNodeDeps.end())
701 continue;
702
703 for (auto *DependantSN : I->second)
704 if (DependantSN->Deps.merge(SN->Deps))
705 ToVisitNext.insert(DependantSN);
706 }
707
708 if (ToVisitNext.empty())
709 break;
710
711 Worklist.append(ToVisitNext.begin(), ToVisitNext.end());
712 }
713 }
714
715 static void propagateFailures(DenseSet<SuperNode *> &FailedNodes,
716 SuperNodeDepsMap &SuperNodeDeps) {
717 if (FailedNodes.empty())
718 return;
719
720 SmallVector<SuperNode *> Worklist(FailedNodes.begin(), FailedNodes.end());
721
722 while (!Worklist.empty()) {
723 auto *SN = Worklist.pop_back_val();
724 auto I = SuperNodeDeps.find(SN);
725 if (I == SuperNodeDeps.end())
726 continue;
727
728 for (auto *DependantSN : I->second)
729 if (FailedNodes.insert(DependantSN).second)
730 Worklist.push_back(DependantSN);
731 }
732 }
733
734 template <typename GetExternalStateFn>
735 static DenseSet<SuperNode *>
736 processExternalDeps(std::vector<std::unique_ptr<SuperNode>> &SNs,
737 GetExternalStateFn &GetExternalState) {
738 DenseSet<SuperNode *> FailedSNs;
739 for (auto &SN : SNs)
740 SN->Deps.visit([&](ContainerId &Container, ElementSet &Elements) {
741 return Elements.remove_if([&](ElementId &Elem) {
742 switch (GetExternalState(Container, Elem)) {
744 return false;
746 return true;
748 FailedSNs.insert(SN.get());
749 return true;
750 };
751 llvm_unreachable("Unknown ExternalState enum");
752 });
753 });
754
755 return FailedSNs;
756 }
757
758 void processReadyOrFailed(std::vector<std::unique_ptr<SuperNode>> &SNs,
759 std::vector<std::unique_ptr<SuperNode>> &Ready,
760 std::vector<std::unique_ptr<SuperNode>> &Failed,
761 SuperNodeDepsMap &SuperNodeDeps,
762 const DenseSet<SuperNode *> &FailedSNs,
763 bool UnmapFromElemToSN) {
764
765 visitWithRemoval(SNs, [&](std::unique_ptr<SuperNode> &SN) {
766 bool SNFailed = FailedSNs.count(SN.get());
767 bool SNReady = SN->Deps.empty();
768
769 if (SNReady || SNFailed) {
770 if (UnmapFromElemToSN)
771 SN->unmapDefsFromThis();
772 auto &ToList = SNFailed ? Failed : Ready;
773 ToList.push_back(std::move(SN));
774 return true;
775 }
776 return false;
777 });
778 }
779
780 std::vector<std::unique_ptr<SuperNode>> PendingSNs;
781 ElemToSuperNodeMap ElemToPendingSN;
782 Coalescer CoalesceToPendingSNs;
783};
784
785} // namespace llvm::orc::detail
786
787#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:1764
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:1668
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:2199
void sort(IteratorTy Start, IteratorTy End)
Definition STLExtras.h:1635
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:1916
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