mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-27 14:52:16 +00:00
Bug 1919025 part 1 - Move dominator tree building code to a new jit/DominatorTree.cpp file. r=mgaudet
Differential Revision: https://phabricator.services.mozilla.com/D222911
This commit is contained in:
parent
0aeedc7129
commit
41e1308d56
185
js/src/jit/DominatorTree.cpp
Normal file
185
js/src/jit/DominatorTree.cpp
Normal file
@ -0,0 +1,185 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jit/DominatorTree.h"
|
||||
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
||||
using namespace js;
|
||||
using namespace js::jit;
|
||||
|
||||
// A Simple, Fast Dominance Algorithm by Cooper et al.
|
||||
// Modified to support empty intersections for OSR, and in RPO.
|
||||
static MBasicBlock* IntersectDominators(MBasicBlock* block1,
|
||||
MBasicBlock* block2) {
|
||||
MBasicBlock* finger1 = block1;
|
||||
MBasicBlock* finger2 = block2;
|
||||
|
||||
MOZ_ASSERT(finger1);
|
||||
MOZ_ASSERT(finger2);
|
||||
|
||||
// In the original paper, the block ID comparisons are on the postorder index.
|
||||
// This implementation iterates in RPO, so the comparisons are reversed.
|
||||
|
||||
// For this function to be called, the block must have multiple predecessors.
|
||||
// If a finger is then found to be self-dominating, it must therefore be
|
||||
// reachable from multiple roots through non-intersecting control flow.
|
||||
// nullptr is returned in this case, to denote an empty intersection.
|
||||
|
||||
while (finger1->id() != finger2->id()) {
|
||||
while (finger1->id() > finger2->id()) {
|
||||
MBasicBlock* idom = finger1->immediateDominator();
|
||||
if (idom == finger1) {
|
||||
return nullptr; // Empty intersection.
|
||||
}
|
||||
finger1 = idom;
|
||||
}
|
||||
|
||||
while (finger2->id() > finger1->id()) {
|
||||
MBasicBlock* idom = finger2->immediateDominator();
|
||||
if (idom == finger2) {
|
||||
return nullptr; // Empty intersection.
|
||||
}
|
||||
finger2 = idom;
|
||||
}
|
||||
}
|
||||
return finger1;
|
||||
}
|
||||
|
||||
static void ComputeImmediateDominators(MIRGraph& graph) {
|
||||
// The default start block is a root and therefore only self-dominates.
|
||||
MBasicBlock* startBlock = graph.entryBlock();
|
||||
startBlock->setImmediateDominator(startBlock);
|
||||
|
||||
// Any OSR block is a root and therefore only self-dominates.
|
||||
MBasicBlock* osrBlock = graph.osrBlock();
|
||||
if (osrBlock) {
|
||||
osrBlock->setImmediateDominator(osrBlock);
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
ReversePostorderIterator block = graph.rpoBegin();
|
||||
|
||||
// For each block in RPO, intersect all dominators.
|
||||
for (; block != graph.rpoEnd(); block++) {
|
||||
// If a node has once been found to have no exclusive dominator,
|
||||
// it will never have an exclusive dominator, so it may be skipped.
|
||||
if (block->immediateDominator() == *block) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A block with no predecessors is not reachable from any entry, so
|
||||
// it self-dominates.
|
||||
if (MOZ_UNLIKELY(block->numPredecessors() == 0)) {
|
||||
block->setImmediateDominator(*block);
|
||||
continue;
|
||||
}
|
||||
|
||||
MBasicBlock* newIdom = block->getPredecessor(0);
|
||||
|
||||
// Find the first common dominator.
|
||||
for (size_t i = 1; i < block->numPredecessors(); i++) {
|
||||
MBasicBlock* pred = block->getPredecessor(i);
|
||||
if (pred->immediateDominator() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newIdom = IntersectDominators(pred, newIdom);
|
||||
|
||||
// If there is no common dominator, the block self-dominates.
|
||||
if (newIdom == nullptr) {
|
||||
block->setImmediateDominator(*block);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newIdom && block->immediateDominator() != newIdom) {
|
||||
block->setImmediateDominator(newIdom);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that all blocks have dominator information.
|
||||
for (MBasicBlockIterator block(graph.begin()); block != graph.end();
|
||||
block++) {
|
||||
MOZ_ASSERT(block->immediateDominator() != nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool jit::BuildDominatorTree(MIRGraph& graph) {
|
||||
MOZ_ASSERT(graph.canBuildDominators());
|
||||
|
||||
ComputeImmediateDominators(graph);
|
||||
|
||||
Vector<MBasicBlock*, 4, JitAllocPolicy> worklist(graph.alloc());
|
||||
|
||||
// Traversing through the graph in post-order means that every non-phi use
|
||||
// of a definition is visited before the def itself. Since a def
|
||||
// dominates its uses, by the time we reach a particular
|
||||
// block, we have processed all of its dominated children, so
|
||||
// block->numDominated() is accurate.
|
||||
for (PostorderIterator i(graph.poBegin()); i != graph.poEnd(); i++) {
|
||||
MBasicBlock* child = *i;
|
||||
MBasicBlock* parent = child->immediateDominator();
|
||||
|
||||
// Dominance is defined such that blocks always dominate themselves.
|
||||
child->addNumDominated(1);
|
||||
|
||||
// If the block only self-dominates, it has no definite parent.
|
||||
// Add it to the worklist as a root for pre-order traversal.
|
||||
// This includes all roots. Order does not matter.
|
||||
if (child == parent) {
|
||||
if (!worklist.append(child)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parent->addImmediatelyDominatedBlock(child)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parent->addNumDominated(child->numDominated());
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// If compiling with OSR, many blocks will self-dominate.
|
||||
// Without OSR, there is only one root block which dominates all.
|
||||
if (!graph.osrBlock()) {
|
||||
MOZ_ASSERT(graph.entryBlock()->numDominated() == graph.numBlocks());
|
||||
}
|
||||
#endif
|
||||
// Now, iterate through the dominator tree in pre-order and annotate every
|
||||
// block with its index in the traversal.
|
||||
size_t index = 0;
|
||||
while (!worklist.empty()) {
|
||||
MBasicBlock* block = worklist.popCopy();
|
||||
block->setDomIndex(index);
|
||||
|
||||
if (!worklist.append(block->immediatelyDominatedBlocksBegin(),
|
||||
block->immediatelyDominatedBlocksEnd())) {
|
||||
return false;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void jit::ClearDominatorTree(MIRGraph& graph) {
|
||||
for (MBasicBlockIterator iter = graph.begin(); iter != graph.end(); iter++) {
|
||||
iter->clearDominatorInfo();
|
||||
}
|
||||
}
|
19
js/src/jit/DominatorTree.h
Normal file
19
js/src/jit/DominatorTree.h
Normal file
@ -0,0 +1,19 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
||||
* vim: set ts=8 sts=2 et sw=2 tw=80:
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef jit_DominatorTree_h
|
||||
#define jit_DominatorTree_h
|
||||
|
||||
namespace js::jit {
|
||||
|
||||
class MIRGraph;
|
||||
|
||||
[[nodiscard]] bool BuildDominatorTree(MIRGraph& graph);
|
||||
void ClearDominatorTree(MIRGraph& graph);
|
||||
|
||||
} // namespace js::jit
|
||||
|
||||
#endif /* jit_DominatorTree_h */
|
@ -23,6 +23,7 @@
|
||||
#include "jit/BranchHinting.h"
|
||||
#include "jit/CodeGenerator.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/DominatorTree.h"
|
||||
#include "jit/EdgeCaseAnalysis.h"
|
||||
#include "jit/EffectiveAddressAnalysis.h"
|
||||
#include "jit/ExecutableAllocator.h"
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "jit/AliasAnalysis.h"
|
||||
#include "jit/CompileInfo.h"
|
||||
#include "jit/DominatorTree.h"
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
#include "util/CheckedArithmetic.h"
|
||||
@ -3027,178 +3028,6 @@ bool jit::RemoveUnmarkedBlocks(const MIRGenerator* mir, MIRGraph& graph,
|
||||
return AccountForCFGChanges(mir, graph, /*updateAliasAnalysis=*/false);
|
||||
}
|
||||
|
||||
// A Simple, Fast Dominance Algorithm by Cooper et al.
|
||||
// Modified to support empty intersections for OSR, and in RPO.
|
||||
static MBasicBlock* IntersectDominators(MBasicBlock* block1,
|
||||
MBasicBlock* block2) {
|
||||
MBasicBlock* finger1 = block1;
|
||||
MBasicBlock* finger2 = block2;
|
||||
|
||||
MOZ_ASSERT(finger1);
|
||||
MOZ_ASSERT(finger2);
|
||||
|
||||
// In the original paper, the block ID comparisons are on the postorder index.
|
||||
// This implementation iterates in RPO, so the comparisons are reversed.
|
||||
|
||||
// For this function to be called, the block must have multiple predecessors.
|
||||
// If a finger is then found to be self-dominating, it must therefore be
|
||||
// reachable from multiple roots through non-intersecting control flow.
|
||||
// nullptr is returned in this case, to denote an empty intersection.
|
||||
|
||||
while (finger1->id() != finger2->id()) {
|
||||
while (finger1->id() > finger2->id()) {
|
||||
MBasicBlock* idom = finger1->immediateDominator();
|
||||
if (idom == finger1) {
|
||||
return nullptr; // Empty intersection.
|
||||
}
|
||||
finger1 = idom;
|
||||
}
|
||||
|
||||
while (finger2->id() > finger1->id()) {
|
||||
MBasicBlock* idom = finger2->immediateDominator();
|
||||
if (idom == finger2) {
|
||||
return nullptr; // Empty intersection.
|
||||
}
|
||||
finger2 = idom;
|
||||
}
|
||||
}
|
||||
return finger1;
|
||||
}
|
||||
|
||||
void jit::ClearDominatorTree(MIRGraph& graph) {
|
||||
for (MBasicBlockIterator iter = graph.begin(); iter != graph.end(); iter++) {
|
||||
iter->clearDominatorInfo();
|
||||
}
|
||||
}
|
||||
|
||||
static void ComputeImmediateDominators(MIRGraph& graph) {
|
||||
// The default start block is a root and therefore only self-dominates.
|
||||
MBasicBlock* startBlock = graph.entryBlock();
|
||||
startBlock->setImmediateDominator(startBlock);
|
||||
|
||||
// Any OSR block is a root and therefore only self-dominates.
|
||||
MBasicBlock* osrBlock = graph.osrBlock();
|
||||
if (osrBlock) {
|
||||
osrBlock->setImmediateDominator(osrBlock);
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
|
||||
while (changed) {
|
||||
changed = false;
|
||||
|
||||
ReversePostorderIterator block = graph.rpoBegin();
|
||||
|
||||
// For each block in RPO, intersect all dominators.
|
||||
for (; block != graph.rpoEnd(); block++) {
|
||||
// If a node has once been found to have no exclusive dominator,
|
||||
// it will never have an exclusive dominator, so it may be skipped.
|
||||
if (block->immediateDominator() == *block) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// A block with no predecessors is not reachable from any entry, so
|
||||
// it self-dominates.
|
||||
if (MOZ_UNLIKELY(block->numPredecessors() == 0)) {
|
||||
block->setImmediateDominator(*block);
|
||||
continue;
|
||||
}
|
||||
|
||||
MBasicBlock* newIdom = block->getPredecessor(0);
|
||||
|
||||
// Find the first common dominator.
|
||||
for (size_t i = 1; i < block->numPredecessors(); i++) {
|
||||
MBasicBlock* pred = block->getPredecessor(i);
|
||||
if (pred->immediateDominator() == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newIdom = IntersectDominators(pred, newIdom);
|
||||
|
||||
// If there is no common dominator, the block self-dominates.
|
||||
if (newIdom == nullptr) {
|
||||
block->setImmediateDominator(*block);
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newIdom && block->immediateDominator() != newIdom) {
|
||||
block->setImmediateDominator(newIdom);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// Assert that all blocks have dominator information.
|
||||
for (MBasicBlockIterator block(graph.begin()); block != graph.end();
|
||||
block++) {
|
||||
MOZ_ASSERT(block->immediateDominator() != nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool jit::BuildDominatorTree(MIRGraph& graph) {
|
||||
MOZ_ASSERT(graph.canBuildDominators());
|
||||
|
||||
ComputeImmediateDominators(graph);
|
||||
|
||||
Vector<MBasicBlock*, 4, JitAllocPolicy> worklist(graph.alloc());
|
||||
|
||||
// Traversing through the graph in post-order means that every non-phi use
|
||||
// of a definition is visited before the def itself. Since a def
|
||||
// dominates its uses, by the time we reach a particular
|
||||
// block, we have processed all of its dominated children, so
|
||||
// block->numDominated() is accurate.
|
||||
for (PostorderIterator i(graph.poBegin()); i != graph.poEnd(); i++) {
|
||||
MBasicBlock* child = *i;
|
||||
MBasicBlock* parent = child->immediateDominator();
|
||||
|
||||
// Dominance is defined such that blocks always dominate themselves.
|
||||
child->addNumDominated(1);
|
||||
|
||||
// If the block only self-dominates, it has no definite parent.
|
||||
// Add it to the worklist as a root for pre-order traversal.
|
||||
// This includes all roots. Order does not matter.
|
||||
if (child == parent) {
|
||||
if (!worklist.append(child)) {
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!parent->addImmediatelyDominatedBlock(child)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
parent->addNumDominated(child->numDominated());
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
// If compiling with OSR, many blocks will self-dominate.
|
||||
// Without OSR, there is only one root block which dominates all.
|
||||
if (!graph.osrBlock()) {
|
||||
MOZ_ASSERT(graph.entryBlock()->numDominated() == graph.numBlocks());
|
||||
}
|
||||
#endif
|
||||
// Now, iterate through the dominator tree in pre-order and annotate every
|
||||
// block with its index in the traversal.
|
||||
size_t index = 0;
|
||||
while (!worklist.empty()) {
|
||||
MBasicBlock* block = worklist.popCopy();
|
||||
block->setDomIndex(index);
|
||||
|
||||
if (!worklist.append(block->immediatelyDominatedBlocksBegin(),
|
||||
block->immediatelyDominatedBlocksEnd())) {
|
||||
return false;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool jit::BuildPhiReverseMapping(MIRGraph& graph) {
|
||||
// Build a mapping such that given a basic block, whose successor has one or
|
||||
// more phis, we can find our specific input to that phi. To make this fast
|
||||
|
@ -81,10 +81,6 @@ void RenumberBlocks(MIRGraph& graph);
|
||||
MIRGraph& graph,
|
||||
uint32_t numMarkedBlocks);
|
||||
|
||||
void ClearDominatorTree(MIRGraph& graph);
|
||||
|
||||
[[nodiscard]] bool BuildDominatorTree(MIRGraph& graph);
|
||||
|
||||
[[nodiscard]] bool BuildPhiReverseMapping(MIRGraph& graph);
|
||||
|
||||
void AssertBasicGraphCoherency(MIRGraph& graph, bool force = false);
|
||||
|
@ -35,6 +35,7 @@ UNIFIED_SOURCES += [
|
||||
"CodeGenerator.cpp",
|
||||
"CompileWrappers.cpp",
|
||||
"Disassemble.cpp",
|
||||
"DominatorTree.cpp",
|
||||
"EdgeCaseAnalysis.cpp",
|
||||
"EffectiveAddressAnalysis.cpp",
|
||||
"ExecutableAllocator.cpp",
|
||||
|
@ -7,6 +7,7 @@
|
||||
#ifndef jsapi_tests_jitTestGVN_h
|
||||
#define jsapi_tests_jitTestGVN_h
|
||||
|
||||
#include "jit/DominatorTree.h"
|
||||
#include "jit/IonAnalysis.h"
|
||||
#include "jit/MIRGenerator.h"
|
||||
#include "jit/MIRGraph.h"
|
||||
|
Loading…
Reference in New Issue
Block a user