Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/js/UbiNodeDominatorTree.h
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2
 * vim: set ts=8 sts=4 et sw=4 tw=99:
3
 * This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#ifndef js_UbiNodeDominatorTree_h
8
#define js_UbiNodeDominatorTree_h
9
10
#include "mozilla/Attributes.h"
11
#include "mozilla/DebugOnly.h"
12
#include "mozilla/Maybe.h"
13
#include "mozilla/Move.h"
14
#include "mozilla/UniquePtr.h"
15
16
#include "js/AllocPolicy.h"
17
#include "js/UbiNode.h"
18
#include "js/UbiNodePostOrder.h"
19
#include "js/Utility.h"
20
#include "js/Vector.h"
21
22
namespace JS {
23
namespace ubi {
24
25
/**
26
 * In a directed graph with a root node `R`, a node `A` is said to "dominate" a
27
 * node `B` iff every path from `R` to `B` contains `A`. A node `A` is said to
28
 * be the "immediate dominator" of a node `B` iff it dominates `B`, is not `B`
29
 * itself, and does not dominate any other nodes which also dominate `B` in
30
 * turn.
31
 *
32
 * If we take every node from a graph `G` and create a new graph `T` with edges
33
 * to each node from its immediate dominator, then `T` is a tree (each node has
34
 * only one immediate dominator, or none if it is the root). This tree is called
35
 * a "dominator tree".
36
 *
37
 * This class represents a dominator tree constructed from a `JS::ubi::Node`
38
 * heap graph. The domination relationship and dominator trees are useful tools
39
 * for analyzing heap graphs because they tell you:
40
 *
41
 *   - Exactly what could be reclaimed by the GC if some node `A` became
42
 *     unreachable: those nodes which are dominated by `A`,
43
 *
44
 *   - The "retained size" of a node in the heap graph, in contrast to its
45
 *     "shallow size". The "shallow size" is the space taken by a node itself,
46
 *     not counting anything it references. The "retained size" of a node is its
47
 *     shallow size plus the size of all the things that would be collected if
48
 *     the original node wasn't (directly or indirectly) referencing them. In
49
 *     other words, the retained size is the shallow size of a node plus the
50
 *     shallow sizes of every other node it dominates. For example, the root
51
 *     node in a binary tree might have a small shallow size that does not take
52
 *     up much space itself, but it dominates the rest of the binary tree and
53
 *     its retained size is therefore significant (assuming no external
54
 *     references into the tree).
55
 *
56
 * The simple, engineered algorithm presented in "A Simple, Fast Dominance
57
 * Algorithm" by Cooper el al[0] is used to find dominators and construct the
58
 * dominator tree. This algorithm runs in O(n^2) time, but is faster in practice
59
 * than alternative algorithms with better theoretical running times, such as
60
 * Lengauer-Tarjan which runs in O(e * log(n)). The big caveat to that statement
61
 * is that Cooper et al found it is faster in practice *on control flow graphs*
62
 * and I'm not convinced that this property also holds on *heap* graphs. That
63
 * said, the implementation of this algorithm is *much* simpler than
64
 * Lengauer-Tarjan and has been found to be fast enough at least for the time
65
 * being.
66
 *
67
 * [0]: http://www.cs.rice.edu/~keith/EMBED/dom.pdf
68
 */
69
class JS_PUBLIC_API(DominatorTree)
70
{
71
  private:
72
    // Types.
73
74
    using PredecessorSets = js::HashMap<Node, NodeSetPtr, js::DefaultHasher<Node>,
75
                                        js::SystemAllocPolicy>;
76
    using NodeToIndexMap = js::HashMap<Node, uint32_t, js::DefaultHasher<Node>,
77
                                       js::SystemAllocPolicy>;
78
    class DominatedSets;
79
80
  public:
81
    class DominatedSetRange;
82
83
    /**
84
     * A pointer to an immediately dominated node.
85
     *
86
     * Don't use this type directly; it is no safer than regular pointers. This
87
     * is only for use indirectly with range-based for loops and
88
     * `DominatedSetRange`.
89
     *
90
     * @see JS::ubi::DominatorTree::getDominatedSet
91
     */
92
    class DominatedNodePtr
93
    {
94
        friend class DominatedSetRange;
95
96
        const JS::ubi::Vector<Node>& postOrder;
97
        const uint32_t* ptr;
98
99
        DominatedNodePtr(const JS::ubi::Vector<Node>& postOrder, const uint32_t* ptr)
100
          : postOrder(postOrder)
101
          , ptr(ptr)
102
0
        { }
103
104
      public:
105
0
        bool operator!=(const DominatedNodePtr& rhs) const { return ptr != rhs.ptr; }
106
0
        void operator++() { ptr++; }
107
0
        const Node& operator*() const { return postOrder[*ptr]; }
108
    };
109
110
    /**
111
     * A range of immediately dominated `JS::ubi::Node`s for use with
112
     * range-based for loops.
113
     *
114
     * @see JS::ubi::DominatorTree::getDominatedSet
115
     */
116
    class DominatedSetRange
117
    {
118
        friend class DominatedSets;
119
120
        const JS::ubi::Vector<Node>& postOrder;
121
        const uint32_t* beginPtr;
122
        const uint32_t* endPtr;
123
124
        DominatedSetRange(JS::ubi::Vector<Node>& postOrder, const uint32_t* begin, const uint32_t* end)
125
          : postOrder(postOrder)
126
          , beginPtr(begin)
127
          , endPtr(end)
128
0
        {
129
0
            MOZ_ASSERT(begin <= end);
130
0
        }
131
132
      public:
133
0
        DominatedNodePtr begin() const {
134
0
            MOZ_ASSERT(beginPtr <= endPtr);
135
0
            return DominatedNodePtr(postOrder, beginPtr);
136
0
        }
137
138
0
        DominatedNodePtr end() const {
139
0
            return DominatedNodePtr(postOrder, endPtr);
140
0
        }
141
142
0
        size_t length() const {
143
0
            MOZ_ASSERT(beginPtr <= endPtr);
144
0
            return endPtr - beginPtr;
145
0
        }
146
147
        /**
148
         * Safely skip ahead `n` dominators in the range, in O(1) time.
149
         *
150
         * Example usage:
151
         *
152
         *     mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
153
         *     if (range.isNothing()) {
154
         *         // Handle unknown nodes however you see fit...
155
         *         return false;
156
         *     }
157
         *
158
         *     // Don't care about the first ten, for whatever reason.
159
         *     range->skip(10);
160
         *     for (const JS::ubi::Node& dominatedNode : *range) {
161
         *         // ...
162
         *     }
163
         */
164
0
        void skip(size_t n) {
165
0
            beginPtr += n;
166
0
            if (beginPtr > endPtr) {
167
0
                beginPtr = endPtr;
168
0
            }
169
0
        }
170
    };
171
172
  private:
173
    /**
174
     * The set of all dominated sets in a dominator tree.
175
     *
176
     * Internally stores the sets in a contiguous array, with a side table of
177
     * indices into that contiguous array to denote the start index of each
178
     * individual set.
179
     */
180
    class DominatedSets
181
    {
182
        JS::ubi::Vector<uint32_t> dominated;
183
        JS::ubi::Vector<uint32_t> indices;
184
185
        DominatedSets(JS::ubi::Vector<uint32_t>&& dominated, JS::ubi::Vector<uint32_t>&& indices)
186
          : dominated(std::move(dominated))
187
          , indices(std::move(indices))
188
0
        { }
189
190
      public:
191
        // DominatedSets is not copy-able.
192
        DominatedSets(const DominatedSets& rhs) = delete;
193
        DominatedSets& operator=(const DominatedSets& rhs) = delete;
194
195
        // DominatedSets is move-able.
196
        DominatedSets(DominatedSets&& rhs)
197
          : dominated(std::move(rhs.dominated))
198
          , indices(std::move(rhs.indices))
199
0
        {
200
0
            MOZ_ASSERT(this != &rhs, "self-move not allowed");
201
0
        }
202
0
        DominatedSets& operator=(DominatedSets&& rhs) {
203
0
            this->~DominatedSets();
204
0
            new (this) DominatedSets(std::move(rhs));
205
0
            return *this;
206
0
        }
207
208
        /**
209
         * Create the DominatedSets given the mapping of a node index to its
210
         * immediate dominator. Returns `Some` on success, `Nothing` on OOM
211
         * failure.
212
         */
213
0
        static mozilla::Maybe<DominatedSets> Create(const JS::ubi::Vector<uint32_t>& doms) {
214
0
            auto length = doms.length();
215
0
            MOZ_ASSERT(length < UINT32_MAX);
216
0
217
0
            // Create a vector `dominated` holding a flattened set of buckets of
218
0
            // immediately dominated children nodes, with a lookup table
219
0
            // `indices` mapping from each node to the beginning of its bucket.
220
0
            //
221
0
            // This has three phases:
222
0
            //
223
0
            // 1. Iterate over the full set of nodes and count up the size of
224
0
            //    each bucket. These bucket sizes are temporarily stored in the
225
0
            //    `indices` vector.
226
0
            //
227
0
            // 2. Convert the `indices` vector to store the cumulative sum of
228
0
            //    the sizes of all buckets before each index, resulting in a
229
0
            //    mapping from node index to one past the end of that node's
230
0
            //    bucket.
231
0
            //
232
0
            // 3. Iterate over the full set of nodes again, filling in bucket
233
0
            //    entries from the end of the bucket's range to its
234
0
            //    beginning. This decrements each index as a bucket entry is
235
0
            //    filled in. After having filled in all of a bucket's entries,
236
0
            //    the index points to the start of the bucket.
237
0
238
0
            JS::ubi::Vector<uint32_t> dominated;
239
0
            JS::ubi::Vector<uint32_t> indices;
240
0
            if (!dominated.growBy(length) || !indices.growBy(length)) {
241
0
                return mozilla::Nothing();
242
0
            }
243
0
244
0
            // 1
245
0
            memset(indices.begin(), 0, length * sizeof(uint32_t));
246
0
            for (uint32_t i = 0; i < length; i++) {
247
0
                indices[doms[i]]++;
248
0
            }
249
0
250
0
            // 2
251
0
            uint32_t sumOfSizes = 0;
252
0
            for (uint32_t i = 0; i < length; i++) {
253
0
                sumOfSizes += indices[i];
254
0
                MOZ_ASSERT(sumOfSizes <= length);
255
0
                indices[i] = sumOfSizes;
256
0
            }
257
0
258
0
            // 3
259
0
            for (uint32_t i = 0; i < length; i++) {
260
0
                auto idxOfDom = doms[i];
261
0
                indices[idxOfDom]--;
262
0
                dominated[indices[idxOfDom]] = i;
263
0
            }
264
0
265
#ifdef DEBUG
266
            // Assert that our buckets are non-overlapping and don't run off the
267
            // end of the vector.
268
            uint32_t lastIndex = 0;
269
            for (uint32_t i = 0; i < length; i++) {
270
                MOZ_ASSERT(indices[i] >= lastIndex);
271
                MOZ_ASSERT(indices[i] < length);
272
                lastIndex = indices[i];
273
            }
274
#endif
275
276
0
            return mozilla::Some(DominatedSets(std::move(dominated), std::move(indices)));
277
0
        }
278
279
        /**
280
         * Get the set of nodes immediately dominated by the node at
281
         * `postOrder[nodeIndex]`.
282
         */
283
0
        DominatedSetRange dominatedSet(JS::ubi::Vector<Node>& postOrder, uint32_t nodeIndex) const {
284
0
            MOZ_ASSERT(postOrder.length() == indices.length());
285
0
            MOZ_ASSERT(nodeIndex < indices.length());
286
0
            auto end = nodeIndex == indices.length() - 1
287
0
                ? dominated.end()
288
0
                : &dominated[indices[nodeIndex + 1]];
289
0
            return DominatedSetRange(postOrder, &dominated[indices[nodeIndex]], end);
290
0
        }
291
    };
292
293
  private:
294
    // Data members.
295
    JS::ubi::Vector<Node> postOrder;
296
    NodeToIndexMap nodeToPostOrderIndex;
297
    JS::ubi::Vector<uint32_t> doms;
298
    DominatedSets dominatedSets;
299
    mozilla::Maybe<JS::ubi::Vector<JS::ubi::Node::Size>> retainedSizes;
300
301
  private:
302
    // We use `UNDEFINED` as a sentinel value in the `doms` vector to signal
303
    // that we haven't found any dominators for the node at the corresponding
304
    // index in `postOrder` yet.
305
    static const uint32_t UNDEFINED = UINT32_MAX;
306
307
    DominatorTree(JS::ubi::Vector<Node>&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex,
308
                  JS::ubi::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
309
        : postOrder(std::move(postOrder))
310
        , nodeToPostOrderIndex(std::move(nodeToPostOrderIndex))
311
        , doms(std::move(doms))
312
        , dominatedSets(std::move(dominatedSets))
313
        , retainedSizes(mozilla::Nothing())
314
0
    { }
315
316
0
    static uint32_t intersect(JS::ubi::Vector<uint32_t>& doms, uint32_t finger1, uint32_t finger2) {
317
0
        while (finger1 != finger2) {
318
0
            if (finger1 < finger2) {
319
0
                finger1 = doms[finger1];
320
0
            } else if (finger2 < finger1) {
321
0
                finger2 = doms[finger2];
322
0
            }
323
0
        }
324
0
        return finger1;
325
0
    }
326
327
    // Do the post order traversal of the heap graph and populate our
328
    // predecessor sets.
329
    static MOZ_MUST_USE bool doTraversal(JSContext* cx, AutoCheckCannotGC& noGC, const Node& root,
330
                                         JS::ubi::Vector<Node>& postOrder,
331
0
                                         PredecessorSets& predecessorSets) {
332
0
        uint32_t nodeCount = 0;
333
0
        auto onNode = [&](const Node& node) {
334
0
            nodeCount++;
335
0
            if (MOZ_UNLIKELY(nodeCount == UINT32_MAX)) {
336
0
                return false;
337
0
            }
338
0
            return postOrder.append(node);
339
0
        };
340
0
341
0
        auto onEdge = [&](const Node& origin, const Edge& edge) {
342
0
            auto p = predecessorSets.lookupForAdd(edge.referent);
343
0
            if (!p) {
344
0
                mozilla::UniquePtr<NodeSet, DeletePolicy<NodeSet>> set(js_new<NodeSet>());
345
0
                if (!set ||
346
0
                    !predecessorSets.add(p, edge.referent, std::move(set)))
347
0
                {
348
0
                    return false;
349
0
                }
350
0
            }
351
0
            MOZ_ASSERT(p && p->value());
352
0
            return p->value()->put(origin);
353
0
        };
354
0
355
0
        PostOrder traversal(cx, noGC);
356
0
        return traversal.addStart(root) &&
357
0
               traversal.traverse(onNode, onEdge);
358
0
    }
359
360
    // Populates the given `map` with an entry for each node to its index in
361
    // `postOrder`.
362
    static MOZ_MUST_USE bool mapNodesToTheirIndices(JS::ubi::Vector<Node>& postOrder,
363
0
                                                    NodeToIndexMap& map) {
364
0
        MOZ_ASSERT(map.empty());
365
0
        MOZ_ASSERT(postOrder.length() < UINT32_MAX);
366
0
        uint32_t length = postOrder.length();
367
0
        if (!map.reserve(length)) {
368
0
            return false;
369
0
        }
370
0
        for (uint32_t i = 0; i < length; i++) {
371
0
            map.putNewInfallible(postOrder[i], i);
372
0
        }
373
0
        return true;
374
0
    }
375
376
    // Convert the Node -> NodeSet predecessorSets to a index -> Vector<index>
377
    // form.
378
    static MOZ_MUST_USE bool convertPredecessorSetsToVectors(
379
        const Node& root,
380
        JS::ubi::Vector<Node>& postOrder,
381
        PredecessorSets& predecessorSets,
382
        NodeToIndexMap& nodeToPostOrderIndex,
383
        JS::ubi::Vector<JS::ubi::Vector<uint32_t>>& predecessorVectors)
384
0
    {
385
0
        MOZ_ASSERT(postOrder.length() < UINT32_MAX);
386
0
        uint32_t length = postOrder.length();
387
0
388
0
        MOZ_ASSERT(predecessorVectors.length() == 0);
389
0
        if (!predecessorVectors.growBy(length)) {
390
0
            return false;
391
0
        }
392
0
393
0
        for (uint32_t i = 0; i < length - 1; i++) {
394
0
            auto& node = postOrder[i];
395
0
            MOZ_ASSERT(node != root,
396
0
                       "Only the last node should be root, since this was a post order traversal.");
397
0
398
0
            auto ptr = predecessorSets.lookup(node);
399
0
            MOZ_ASSERT(ptr,
400
0
                       "Because this isn't the root, it had better have predecessors, or else how "
401
0
                       "did we even find it.");
402
0
403
0
            auto& predecessors = ptr->value();
404
0
            if (!predecessorVectors[i].reserve(predecessors->count())) {
405
0
                return false;
406
0
            }
407
0
            for (auto range = predecessors->all(); !range.empty(); range.popFront()) {
408
0
                auto ptr = nodeToPostOrderIndex.lookup(range.front());
409
0
                MOZ_ASSERT(ptr);
410
0
                predecessorVectors[i].infallibleAppend(ptr->value());
411
0
            }
412
0
        }
413
0
        predecessorSets.clearAndCompact();
414
0
        return true;
415
0
    }
416
417
    // Initialize `doms` such that the immediate dominator of the `root` is the
418
    // `root` itself and all others are `UNDEFINED`.
419
    static MOZ_MUST_USE bool initializeDominators(JS::ubi::Vector<uint32_t>& doms,
420
0
                                                  uint32_t length) {
421
0
        MOZ_ASSERT(doms.length() == 0);
422
0
        if (!doms.growByUninitialized(length)) {
423
0
            return false;
424
0
        }
425
0
        doms[length - 1] = length - 1;
426
0
        for (uint32_t i = 0; i < length - 1; i++) {
427
0
            doms[i] = UNDEFINED;
428
0
        }
429
0
        return true;
430
0
    }
431
432
0
    void assertSanity() const {
433
0
        MOZ_ASSERT(postOrder.length() == doms.length());
434
0
        MOZ_ASSERT(postOrder.length() == nodeToPostOrderIndex.count());
435
0
        MOZ_ASSERT_IF(retainedSizes.isSome(), postOrder.length() == retainedSizes->length());
436
0
    }
437
438
0
    MOZ_MUST_USE bool computeRetainedSizes(mozilla::MallocSizeOf mallocSizeOf) {
439
0
        MOZ_ASSERT(retainedSizes.isNothing());
440
0
        auto length = postOrder.length();
441
0
442
0
        retainedSizes.emplace();
443
0
        if (!retainedSizes->growBy(length)) {
444
0
            retainedSizes = mozilla::Nothing();
445
0
            return false;
446
0
        }
447
0
448
0
        // Iterate in forward order so that we know all of a node's children in
449
0
        // the dominator tree have already had their retained size
450
0
        // computed. Then we can simply say that the retained size of a node is
451
0
        // its shallow size (JS::ubi::Node::size) plus the retained sizes of its
452
0
        // immediate children in the tree.
453
0
454
0
        for (uint32_t i = 0; i < length; i++) {
455
0
            auto size = postOrder[i].size(mallocSizeOf);
456
0
457
0
            for (const auto& dominated : dominatedSets.dominatedSet(postOrder, i)) {
458
0
                // The root node dominates itself, but shouldn't contribute to
459
0
                // its own retained size.
460
0
                if (dominated == postOrder[length - 1]) {
461
0
                    MOZ_ASSERT(i == length - 1);
462
0
                    continue;
463
0
                }
464
0
465
0
                auto ptr = nodeToPostOrderIndex.lookup(dominated);
466
0
                MOZ_ASSERT(ptr);
467
0
                auto idxOfDominated = ptr->value();
468
0
                MOZ_ASSERT(idxOfDominated < i);
469
0
                size += retainedSizes.ref()[idxOfDominated];
470
0
            }
471
0
472
0
            retainedSizes.ref()[i] = size;
473
0
        }
474
0
475
0
        return true;
476
0
    }
477
478
  public:
479
    // DominatorTree is not copy-able.
480
    DominatorTree(const DominatorTree&) = delete;
481
    DominatorTree& operator=(const DominatorTree&) = delete;
482
483
    // DominatorTree is move-able.
484
    DominatorTree(DominatorTree&& rhs)
485
      : postOrder(std::move(rhs.postOrder))
486
      , nodeToPostOrderIndex(std::move(rhs.nodeToPostOrderIndex))
487
      , doms(std::move(rhs.doms))
488
      , dominatedSets(std::move(rhs.dominatedSets))
489
      , retainedSizes(std::move(rhs.retainedSizes))
490
0
    {
491
0
        MOZ_ASSERT(this != &rhs, "self-move is not allowed");
492
0
    }
493
0
    DominatorTree& operator=(DominatorTree&& rhs) {
494
0
        this->~DominatorTree();
495
0
        new (this) DominatorTree(std::move(rhs));
496
0
        return *this;
497
0
    }
498
499
    /**
500
     * Construct a `DominatorTree` of the heap graph visible from `root`. The
501
     * `root` is also used as the root of the resulting dominator tree.
502
     *
503
     * The resulting `DominatorTree` instance must not outlive the
504
     * `JS::ubi::Node` graph it was constructed from.
505
     *
506
     *   - For `JS::ubi::Node` graphs backed by the live heap graph, this means
507
     *     that the `DominatorTree`'s lifetime _must_ be contained within the
508
     *     scope of the provided `AutoCheckCannotGC` reference because a GC will
509
     *     invalidate the nodes.
510
     *
511
     *   - For `JS::ubi::Node` graphs backed by some other offline structure
512
     *     provided by the embedder, the resulting `DominatorTree`'s lifetime is
513
     *     bounded by that offline structure's lifetime.
514
     *
515
     * In practice, this means that within SpiderMonkey we must treat
516
     * `DominatorTree` as if it were backed by the live heap graph and trust
517
     * that embedders with knowledge of the graph's implementation will do the
518
     * Right Thing.
519
     *
520
     * Returns `mozilla::Nothing()` on OOM failure. It is the caller's
521
     * responsibility to handle and report the OOM.
522
     */
523
    static mozilla::Maybe<DominatorTree>
524
0
    Create(JSContext* cx, AutoCheckCannotGC& noGC, const Node& root) {
525
0
        JS::ubi::Vector<Node> postOrder;
526
0
        PredecessorSets predecessorSets;
527
0
        if (!doTraversal(cx, noGC, root, postOrder, predecessorSets)) {
528
0
            return mozilla::Nothing();
529
0
        }
530
0
531
0
        MOZ_ASSERT(postOrder.length() < UINT32_MAX);
532
0
        uint32_t length = postOrder.length();
533
0
        MOZ_ASSERT(postOrder[length - 1] == root);
534
0
535
0
        // From here on out we wish to avoid hash table lookups, and we use
536
0
        // indices into `postOrder` instead of actual nodes wherever
537
0
        // possible. This greatly improves the performance of this
538
0
        // implementation, but we have to pay a little bit of upfront cost to
539
0
        // convert our data structures to play along first.
540
0
541
0
        NodeToIndexMap nodeToPostOrderIndex(postOrder.length());
542
0
        if (!mapNodesToTheirIndices(postOrder, nodeToPostOrderIndex)) {
543
0
            return mozilla::Nothing();
544
0
        }
545
0
546
0
        JS::ubi::Vector<JS::ubi::Vector<uint32_t>> predecessorVectors;
547
0
        if (!convertPredecessorSetsToVectors(root, postOrder, predecessorSets, nodeToPostOrderIndex,
548
0
                                             predecessorVectors))
549
0
            return mozilla::Nothing();
550
0
551
0
        JS::ubi::Vector<uint32_t> doms;
552
0
        if (!initializeDominators(doms, length)) {
553
0
            return mozilla::Nothing();
554
0
        }
555
0
556
0
        bool changed = true;
557
0
        while (changed) {
558
0
            changed = false;
559
0
560
0
            // Iterate over the non-root nodes in reverse post order.
561
0
            for (uint32_t indexPlusOne = length - 1; indexPlusOne > 0; indexPlusOne--) {
562
0
                MOZ_ASSERT(postOrder[indexPlusOne - 1] != root);
563
0
564
0
                // Take the intersection of every predecessor's dominator set;
565
0
                // that is the current best guess at the immediate dominator for
566
0
                // this node.
567
0
568
0
                uint32_t newIDomIdx = UNDEFINED;
569
0
570
0
                auto& predecessors = predecessorVectors[indexPlusOne - 1];
571
0
                auto range = predecessors.all();
572
0
                for ( ; !range.empty(); range.popFront()) {
573
0
                    auto idx = range.front();
574
0
                    if (doms[idx] != UNDEFINED) {
575
0
                        newIDomIdx = idx;
576
0
                        break;
577
0
                    }
578
0
                }
579
0
580
0
                MOZ_ASSERT(newIDomIdx != UNDEFINED,
581
0
                           "Because the root is initialized to dominate itself and is the first "
582
0
                           "node in every path, there must exist a predecessor to this node that "
583
0
                           "also has a dominator.");
584
0
585
0
                for ( ; !range.empty(); range.popFront()) {
586
0
                    auto idx = range.front();
587
0
                    if (doms[idx] != UNDEFINED) {
588
0
                        newIDomIdx = intersect(doms, newIDomIdx, idx);
589
0
                    }
590
0
                }
591
0
592
0
                // If the immediate dominator changed, we will have to do
593
0
                // another pass of the outer while loop to continue the forward
594
0
                // dataflow.
595
0
                if (newIDomIdx != doms[indexPlusOne - 1]) {
596
0
                    doms[indexPlusOne - 1] = newIDomIdx;
597
0
                    changed = true;
598
0
                }
599
0
            }
600
0
        }
601
0
602
0
        auto maybeDominatedSets = DominatedSets::Create(doms);
603
0
        if (maybeDominatedSets.isNothing()) {
604
0
            return mozilla::Nothing();
605
0
        }
606
0
607
0
        return mozilla::Some(DominatorTree(std::move(postOrder),
608
0
                                           std::move(nodeToPostOrderIndex),
609
0
                                           std::move(doms),
610
0
                                           std::move(*maybeDominatedSets)));
611
0
    }
612
613
    /**
614
     * Get the root node for this dominator tree.
615
     */
616
0
    const Node& root() const {
617
0
        return postOrder[postOrder.length() - 1];
618
0
    }
619
620
    /**
621
     * Return the immediate dominator of the given `node`. If `node` was not
622
     * reachable from the `root` that this dominator tree was constructed from,
623
     * then return the null `JS::ubi::Node`.
624
     */
625
0
    Node getImmediateDominator(const Node& node) const {
626
0
        assertSanity();
627
0
        auto ptr = nodeToPostOrderIndex.lookup(node);
628
0
        if (!ptr) {
629
0
            return Node();
630
0
        }
631
0
632
0
        auto idx = ptr->value();
633
0
        MOZ_ASSERT(idx < postOrder.length());
634
0
        return postOrder[doms[idx]];
635
0
    }
636
637
    /**
638
     * Get the set of nodes immediately dominated by the given `node`. If `node`
639
     * is not a member of this dominator tree, return `Nothing`.
640
     *
641
     * Example usage:
642
     *
643
     *     mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
644
     *     if (range.isNothing()) {
645
     *         // Handle unknown node however you see fit...
646
     *         return false;
647
     *     }
648
     *
649
     *     for (const JS::ubi::Node& dominatedNode : *range) {
650
     *         // Do something with each immediately dominated node...
651
     *     }
652
     */
653
0
    mozilla::Maybe<DominatedSetRange> getDominatedSet(const Node& node) {
654
0
        assertSanity();
655
0
        auto ptr = nodeToPostOrderIndex.lookup(node);
656
0
        if (!ptr) {
657
0
            return mozilla::Nothing();
658
0
        }
659
0
660
0
        auto idx = ptr->value();
661
0
        MOZ_ASSERT(idx < postOrder.length());
662
0
        return mozilla::Some(dominatedSets.dominatedSet(postOrder, idx));
663
0
    }
664
665
    /**
666
     * Get the retained size of the given `node`. The size is placed in
667
     * `outSize`, or 0 if `node` is not a member of the dominator tree. Returns
668
     * false on OOM failure, leaving `outSize` unchanged.
669
     */
670
    MOZ_MUST_USE bool getRetainedSize(const Node& node, mozilla::MallocSizeOf mallocSizeOf,
671
0
                                      Node::Size& outSize) {
672
0
        assertSanity();
673
0
        auto ptr = nodeToPostOrderIndex.lookup(node);
674
0
        if (!ptr) {
675
0
            outSize = 0;
676
0
            return true;
677
0
        }
678
0
679
0
        if (retainedSizes.isNothing() && !computeRetainedSizes(mallocSizeOf)) {
680
0
            return false;
681
0
        }
682
0
683
0
        auto idx = ptr->value();
684
0
        MOZ_ASSERT(idx < postOrder.length());
685
0
        outSize = retainedSizes.ref()[idx];
686
0
        return true;
687
0
    }
688
};
689
690
} // namespace ubi
691
} // namespace JS
692
693
#endif // js_UbiNodeDominatorTree_h