UbiNodeDominatorTree.h 25 KB

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