mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1225941 - Add a method for getting the set of nodes immediately dominated by another node; r=sfink (d1507ec58f)
This commit is contained in:
@@ -69,19 +69,227 @@ namespace ubi {
|
||||
class JS_EXPORT_API(DominatorTree)
|
||||
{
|
||||
private:
|
||||
// Type aliases.
|
||||
// Types.
|
||||
|
||||
using NodeSet = js::HashSet<Node, js::DefaultHasher<Node>, js::SystemAllocPolicy>;
|
||||
using NodeSetPtr = mozilla::UniquePtr<NodeSet, JS::DeletePolicy<NodeSet>>;
|
||||
using PredecessorSets = js::HashMap<Node, NodeSetPtr, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
using NodeToIndexMap = js::HashMap<Node, uint32_t, js::DefaultHasher<Node>,
|
||||
js::SystemAllocPolicy>;
|
||||
class DominatedSets;
|
||||
|
||||
public:
|
||||
class DominatedSetRange;
|
||||
|
||||
/**
|
||||
* A pointer to an immediately dominated node.
|
||||
*
|
||||
* Don't use this type directly; it is no safer than regular pointers. This
|
||||
* is only for use indirectly with range-based for loops and
|
||||
* `DominatedSetRange`.
|
||||
*
|
||||
* @see JS::ubi::DominatorTree::getDominatedSet
|
||||
*/
|
||||
class DominatedNodePtr
|
||||
{
|
||||
friend class DominatedSetRange;
|
||||
|
||||
const mozilla::Vector<Node>& postOrder;
|
||||
const uint32_t* ptr;
|
||||
|
||||
DominatedNodePtr(const mozilla::Vector<Node>& postOrder, const uint32_t* ptr)
|
||||
: postOrder(postOrder)
|
||||
, ptr(ptr)
|
||||
{ }
|
||||
|
||||
public:
|
||||
bool operator!=(const DominatedNodePtr& rhs) const { return ptr != rhs.ptr; }
|
||||
void operator++() { ptr++; }
|
||||
const Node& operator*() const { return postOrder[*ptr]; }
|
||||
};
|
||||
|
||||
/**
|
||||
* A range of immediately dominated `JS::ubi::Node`s for use with
|
||||
* range-based for loops.
|
||||
*
|
||||
* @see JS::ubi::DominatorTree::getDominatedSet
|
||||
*/
|
||||
class DominatedSetRange
|
||||
{
|
||||
friend class DominatedSets;
|
||||
|
||||
const mozilla::Vector<Node>& postOrder;
|
||||
const uint32_t* beginPtr;
|
||||
const uint32_t* endPtr;
|
||||
|
||||
DominatedSetRange(mozilla::Vector<Node>& postOrder, const uint32_t* begin, const uint32_t* end)
|
||||
: postOrder(postOrder)
|
||||
, beginPtr(begin)
|
||||
, endPtr(end)
|
||||
{
|
||||
MOZ_ASSERT(begin <= end);
|
||||
}
|
||||
|
||||
public:
|
||||
DominatedNodePtr begin() const {
|
||||
MOZ_ASSERT(beginPtr <= endPtr);
|
||||
return DominatedNodePtr(postOrder, beginPtr);
|
||||
}
|
||||
|
||||
DominatedNodePtr end() const {
|
||||
return DominatedNodePtr(postOrder, endPtr);
|
||||
}
|
||||
|
||||
/**
|
||||
* Safely skip ahead `n` dominators in the range, in O(1) time.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
|
||||
* if (range.isNothing()) {
|
||||
* // Handle unknown nodes however you see fit...
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* // Don't care about the first ten, for whatever reason.
|
||||
* range->skip(10);
|
||||
* for (const JS::ubi::Node& dominatedNode : *range) {
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
void skip(size_t n) {
|
||||
beginPtr += n;
|
||||
if (beginPtr > endPtr)
|
||||
beginPtr = endPtr;
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
/**
|
||||
* The set of all dominated sets in a dominator tree.
|
||||
*
|
||||
* Internally stores the sets in a contiguous array, with a side table of
|
||||
* indices into that contiguous array to denote the start index of each
|
||||
* individual set.
|
||||
*/
|
||||
class DominatedSets
|
||||
{
|
||||
mozilla::Vector<uint32_t> dominated;
|
||||
mozilla::Vector<uint32_t> indices;
|
||||
|
||||
DominatedSets(mozilla::Vector<uint32_t>&& dominated, mozilla::Vector<uint32_t>&& indices)
|
||||
: dominated(mozilla::Move(dominated))
|
||||
, indices(mozilla::Move(indices))
|
||||
{ }
|
||||
|
||||
public:
|
||||
// DominatedSets is not copy-able.
|
||||
DominatedSets(const DominatedSets& rhs) = delete;
|
||||
DominatedSets& operator=(const DominatedSets& rhs) = delete;
|
||||
|
||||
// DominatedSets is move-able.
|
||||
DominatedSets(DominatedSets&& rhs)
|
||||
: dominated(mozilla::Move(rhs.dominated))
|
||||
, indices(mozilla::Move(rhs.indices))
|
||||
{
|
||||
MOZ_ASSERT(this != &rhs, "self-move not allowed");
|
||||
}
|
||||
DominatedSets& operator=(DominatedSets&& rhs) {
|
||||
this->~DominatedSets();
|
||||
new (this) DominatedSets(mozilla::Move(rhs));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the DominatedSets given the mapping of a node index to its
|
||||
* immediate dominator. Returns `Some` on success, `Nothing` on OOM
|
||||
* failure.
|
||||
*/
|
||||
static mozilla::Maybe<DominatedSets> Create(const mozilla::Vector<uint32_t>& doms) {
|
||||
auto length = doms.length();
|
||||
MOZ_ASSERT(length < UINT32_MAX);
|
||||
|
||||
// Create a vector `dominated` holding a flattened set of buckets of
|
||||
// immediately dominated children nodes, with a lookup table
|
||||
// `indices` mapping from each node to the beginning of its bucket.
|
||||
//
|
||||
// This has three phases:
|
||||
//
|
||||
// 1. Iterate over the full set of nodes and count up the size of
|
||||
// each bucket. These bucket sizes are temporarily stored in the
|
||||
// `indices` vector.
|
||||
//
|
||||
// 2. Convert the `indices` vector to store the cumulative sum of
|
||||
// the sizes of all buckets before each index, resulting in a
|
||||
// mapping from node index to one past the end of that node's
|
||||
// bucket.
|
||||
//
|
||||
// 3. Iterate over the full set of nodes again, filling in bucket
|
||||
// entries from the end of the bucket's range to its
|
||||
// beginning. This decrements each index as a bucket entry is
|
||||
// filled in. After having filled in all of a bucket's entries,
|
||||
// the index points to the start of the bucket.
|
||||
|
||||
mozilla::Vector<uint32_t> dominated;
|
||||
mozilla::Vector<uint32_t> indices;
|
||||
if (!dominated.growBy(length) || !indices.growBy(length))
|
||||
return mozilla::Nothing();
|
||||
|
||||
// 1
|
||||
memset(indices.begin(), 0, length * sizeof(uint32_t));
|
||||
for (uint32_t i = 0; i < length; i++)
|
||||
indices[doms[i]]++;
|
||||
|
||||
// 2
|
||||
uint32_t sumOfSizes = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
sumOfSizes += indices[i];
|
||||
MOZ_ASSERT(sumOfSizes <= length);
|
||||
indices[i] = sumOfSizes;
|
||||
}
|
||||
|
||||
// 3
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
auto idxOfDom = doms[i];
|
||||
indices[idxOfDom]--;
|
||||
dominated[indices[idxOfDom]] = i;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that our buckets are non-overlapping and don't run off the
|
||||
// end of the vector.
|
||||
uint32_t lastIndex = 0;
|
||||
for (uint32_t i = 0; i < length; i++) {
|
||||
MOZ_ASSERT(indices[i] >= lastIndex);
|
||||
MOZ_ASSERT(indices[i] < length);
|
||||
lastIndex = indices[i];
|
||||
}
|
||||
#endif
|
||||
|
||||
return mozilla::Some(DominatedSets(mozilla::Move(dominated), mozilla::Move(indices)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of nodes immediately dominated by the node at
|
||||
* `postOrder[nodeIndex]`.
|
||||
*/
|
||||
DominatedSetRange dominatedSet(mozilla::Vector<Node>& postOrder, uint32_t nodeIndex) const {
|
||||
MOZ_ASSERT(postOrder.length() == indices.length());
|
||||
MOZ_ASSERT(nodeIndex < indices.length());
|
||||
auto end = nodeIndex == indices.length() - 1
|
||||
? dominated.end()
|
||||
: &dominated[indices[nodeIndex + 1]];
|
||||
return DominatedSetRange(postOrder, &dominated[indices[nodeIndex]], end);
|
||||
}
|
||||
};
|
||||
|
||||
private:
|
||||
// Data members.
|
||||
mozilla::Vector<Node> postOrder;
|
||||
NodeToIndexMap nodeToPostOrderIndex;
|
||||
mozilla::Vector<uint32_t> doms;
|
||||
DominatedSets dominatedSets;
|
||||
|
||||
private:
|
||||
// We use `UNDEFINED` as a sentinel value in the `doms` vector to signal
|
||||
@@ -90,10 +298,11 @@ class JS_EXPORT_API(DominatorTree)
|
||||
static const uint32_t UNDEFINED = UINT32_MAX;
|
||||
|
||||
DominatorTree(mozilla::Vector<Node>&& postOrder, NodeToIndexMap&& nodeToPostOrderIndex,
|
||||
mozilla::Vector<uint32_t>&& doms)
|
||||
mozilla::Vector<uint32_t>&& doms, DominatedSets&& dominatedSets)
|
||||
: postOrder(mozilla::Move(postOrder))
|
||||
, nodeToPostOrderIndex(mozilla::Move(nodeToPostOrderIndex))
|
||||
, doms(mozilla::Move(doms))
|
||||
, dominatedSets(mozilla::Move(dominatedSets))
|
||||
{ }
|
||||
|
||||
static uint32_t intersect(mozilla::Vector<uint32_t>& doms, uint32_t finger1, uint32_t finger2) {
|
||||
@@ -218,6 +427,7 @@ class JS_EXPORT_API(DominatorTree)
|
||||
: postOrder(mozilla::Move(rhs.postOrder))
|
||||
, nodeToPostOrderIndex(mozilla::Move(rhs.nodeToPostOrderIndex))
|
||||
, doms(mozilla::Move(rhs.doms))
|
||||
, dominatedSets(mozilla::Move(rhs.dominatedSets))
|
||||
{
|
||||
MOZ_ASSERT(this != &rhs, "self-move is not allowed");
|
||||
}
|
||||
@@ -326,9 +536,14 @@ class JS_EXPORT_API(DominatorTree)
|
||||
}
|
||||
}
|
||||
|
||||
auto maybeDominatedSets = DominatedSets::Create(doms);
|
||||
if (maybeDominatedSets.isNothing())
|
||||
return mozilla::Nothing();
|
||||
|
||||
return mozilla::Some(DominatorTree(mozilla::Move(postOrder),
|
||||
mozilla::Move(nodeToPostOrderIndex),
|
||||
mozilla::Move(doms)));
|
||||
mozilla::Move(doms),
|
||||
mozilla::Move(*maybeDominatedSets)));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -346,6 +561,33 @@ class JS_EXPORT_API(DominatorTree)
|
||||
MOZ_ASSERT(idx < postOrder.length());
|
||||
return postOrder[doms[idx]];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the set of nodes immediately dominated by the given `node`. If `node`
|
||||
* is not a member of this dominator tree, return `Nothing`.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* mozilla::Maybe<DominatedSetRange> range = myDominatorTree.getDominatedSet(myNode);
|
||||
* if (range.isNothing()) {
|
||||
* // Handle unknown node however you see fit...
|
||||
* return false;
|
||||
* }
|
||||
*
|
||||
* for (const JS::ubi::Node& dominatedNode : *range) {
|
||||
* // Do something with each immediately dominated node...
|
||||
* }
|
||||
*/
|
||||
mozilla::Maybe<DominatedSetRange> getDominatedSet(const Node& node) {
|
||||
assertSanity();
|
||||
auto ptr = nodeToPostOrderIndex.lookup(node);
|
||||
if (!ptr)
|
||||
return mozilla::Nothing();
|
||||
|
||||
auto idx = ptr->value();
|
||||
MOZ_ASSERT(idx < postOrder.length());
|
||||
return mozilla::Some(dominatedSets.dominatedSet(postOrder, idx));
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace ubi
|
||||
|
||||
@@ -519,58 +519,68 @@ BEGIN_TEST(test_JS_ubi_DominatorTree)
|
||||
// graph when computing the dominator tree.
|
||||
FakeNode m('m');
|
||||
CHECK(tree.getImmediateDominator(&m) == JS::ubi::Node());
|
||||
CHECK(tree.getDominatedSet(&m).isNothing());
|
||||
|
||||
fprintf(stderr, "r's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&r).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&r) == JS::ubi::Node(&r));
|
||||
struct {
|
||||
FakeNode& dominated;
|
||||
FakeNode& dominator;
|
||||
} domination[] = {
|
||||
{r, r},
|
||||
{a, r},
|
||||
{b, r},
|
||||
{c, r},
|
||||
{d, r},
|
||||
{e, r},
|
||||
{f, c},
|
||||
{g, c},
|
||||
{h, r},
|
||||
{i, r},
|
||||
{j, g},
|
||||
{k, r},
|
||||
{l, d}
|
||||
};
|
||||
|
||||
fprintf(stderr, "a's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&a).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&a) == JS::ubi::Node(&r));
|
||||
for (auto& relation : domination) {
|
||||
// Test immediate dominator.
|
||||
fprintf(stderr,
|
||||
"%c's immediate dominator is %c\n",
|
||||
relation.dominated.name,
|
||||
tree.getImmediateDominator(&relation.dominator).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&relation.dominated) == JS::ubi::Node(&relation.dominator));
|
||||
|
||||
fprintf(stderr, "b's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&b).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&b) == JS::ubi::Node(&r));
|
||||
// Test the dominated set. Build up the expected dominated set based on
|
||||
// the set of nodes immediately dominated by this one in `domination`,
|
||||
// then iterate over the actual dominated set and check against the
|
||||
// expected set.
|
||||
|
||||
fprintf(stderr, "c's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&c).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&c) == JS::ubi::Node(&r));
|
||||
auto& node = relation.dominated;
|
||||
fprintf(stderr, "Checking %c's dominated set:\n", node.name);
|
||||
|
||||
fprintf(stderr, "d's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&d).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&d) == JS::ubi::Node(&r));
|
||||
js::HashSet<char> expectedDominatedSet(cx);
|
||||
CHECK(expectedDominatedSet.init());
|
||||
for (auto& rel : domination) {
|
||||
if (&rel.dominator == &node) {
|
||||
fprintf(stderr, " Expecting %c\n", rel.dominated.name);
|
||||
CHECK(expectedDominatedSet.putNew(rel.dominated.name));
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stderr, "e's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&e).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&e) == JS::ubi::Node(&r));
|
||||
auto maybeActualDominatedSet = tree.getDominatedSet(&node);
|
||||
CHECK(maybeActualDominatedSet.isSome());
|
||||
auto& actualDominatedSet = *maybeActualDominatedSet;
|
||||
|
||||
fprintf(stderr, "f's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&f).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&f) == JS::ubi::Node(&c));
|
||||
for (const auto& dominated : actualDominatedSet) {
|
||||
fprintf(stderr, " Found %c\n", dominated.as<FakeNode>()->name);
|
||||
CHECK(expectedDominatedSet.has(dominated.as<FakeNode>()->name));
|
||||
expectedDominatedSet.remove(dominated.as<FakeNode>()->name);
|
||||
}
|
||||
|
||||
fprintf(stderr, "g's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&g).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&g) == JS::ubi::Node(&c));
|
||||
// Ensure we found them all and aren't still expecting nodes we never
|
||||
// got.
|
||||
CHECK(expectedDominatedSet.count() == 0);
|
||||
|
||||
fprintf(stderr, "h's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&h).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&h) == JS::ubi::Node(&r));
|
||||
|
||||
fprintf(stderr, "i's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&i).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&i) == JS::ubi::Node(&r));
|
||||
|
||||
fprintf(stderr, "j's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&j).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&j) == JS::ubi::Node(&g));
|
||||
|
||||
fprintf(stderr, "k's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&k).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&k) == JS::ubi::Node(&r));
|
||||
|
||||
fprintf(stderr, "l's immediate dominator is %c\n",
|
||||
tree.getImmediateDominator(&l).as<FakeNode>()->name);
|
||||
CHECK(tree.getImmediateDominator(&l) == JS::ubi::Node(&d));
|
||||
fprintf(stderr, "Done checking %c's dominated set.\n\n", node.name);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user