mirror of
https://github.com/dart-lang/sdk
synced 2024-09-20 03:51:33 +00:00
Optimize conditional branches that have same true/false targets.
Branches that have the same true/false target, or where both target blocks reach the same common join block via empty blocks can be replaced by a goto to the common join block. In code like this var a = unknown(); var b = null; if (a == null || b == null) { ... } it eliminates the test (a == null) if b is known to be null. Until now, the compiler could only eliminate the test for b, if a was known. R=vegorov@google.com Review URL: https://codereview.chromium.org//261823005 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@35669 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
parent
333cd2ad67
commit
07fcee05c8
|
@ -466,7 +466,8 @@ static bool CompileParsedFunctionHelper(ParsedFunction* parsed_function,
|
|||
|
||||
if (FLAG_constant_propagation) {
|
||||
// Constant propagation can use information from range analysis to
|
||||
// find unreachable branch targets.
|
||||
// find unreachable branch targets and eliminate branches that have
|
||||
// the same true- and false-target.
|
||||
ConstantPropagator::OptimizeBranches(flow_graph);
|
||||
DEBUG_ASSERT(flow_graph->VerifyUseLists());
|
||||
}
|
||||
|
|
|
@ -7169,6 +7169,7 @@ void ConstantPropagator::OptimizeBranches(FlowGraph* graph) {
|
|||
cp.Analyze();
|
||||
cp.VisitBranches();
|
||||
cp.Transform();
|
||||
cp.EliminateRedundantBranches();
|
||||
}
|
||||
|
||||
|
||||
|
@ -8451,6 +8452,85 @@ void ConstantPropagator::VisitBranches() {
|
|||
}
|
||||
|
||||
|
||||
static bool IsEmptyBlock(BlockEntryInstr* block) {
|
||||
return block->next()->IsGoto() &&
|
||||
(!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL));
|
||||
}
|
||||
|
||||
|
||||
// Traverses a chain of empty blocks and returns the first reachable non-empty
|
||||
// block that is not dominated by the start block. The empty blocks are added
|
||||
// to the supplied bit vector.
|
||||
static BlockEntryInstr* FindFirstNonEmptySuccessor(
|
||||
TargetEntryInstr* block,
|
||||
BitVector* empty_blocks) {
|
||||
BlockEntryInstr* current = block;
|
||||
while (IsEmptyBlock(current) && block->Dominates(current)) {
|
||||
ASSERT(!block->IsJoinEntry() || (block->AsJoinEntry()->phis() == NULL));
|
||||
empty_blocks->Add(current->preorder_number());
|
||||
current = current->next()->AsGoto()->successor();
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
|
||||
void ConstantPropagator::EliminateRedundantBranches() {
|
||||
// Canonicalize branches that have no side-effects and where true- and
|
||||
// false-targets are the same.
|
||||
bool changed = false;
|
||||
BitVector* empty_blocks = new BitVector(graph_->preorder().length());
|
||||
for (BlockIterator b = graph_->postorder_iterator();
|
||||
!b.Done();
|
||||
b.Advance()) {
|
||||
BlockEntryInstr* block = b.Current();
|
||||
BranchInstr* branch = block->last_instruction()->AsBranch();
|
||||
empty_blocks->Clear();
|
||||
if ((branch != NULL) && branch->Effects().IsNone()) {
|
||||
ASSERT(branch->previous() != NULL); // Not already eliminated.
|
||||
BlockEntryInstr* if_true =
|
||||
FindFirstNonEmptySuccessor(branch->true_successor(), empty_blocks);
|
||||
BlockEntryInstr* if_false =
|
||||
FindFirstNonEmptySuccessor(branch->false_successor(), empty_blocks);
|
||||
if (if_true == if_false) {
|
||||
// Replace the branch with a jump to the common successor.
|
||||
// Drop the comparison, which does not have side effects
|
||||
JoinEntryInstr* join = if_true->AsJoinEntry();
|
||||
if (join->phis() == NULL) {
|
||||
GotoInstr* jump = new GotoInstr(if_true->AsJoinEntry());
|
||||
jump->InheritDeoptTarget(branch);
|
||||
|
||||
Instruction* previous = branch->previous();
|
||||
branch->set_previous(NULL);
|
||||
previous->LinkTo(jump);
|
||||
|
||||
// Remove uses from branch and all the empty blocks that
|
||||
// are now unreachable.
|
||||
branch->UnuseAllInputs();
|
||||
for (BitVector::Iterator it(empty_blocks); !it.Done(); it.Advance()) {
|
||||
BlockEntryInstr* empty_block = graph_->preorder()[it.Current()];
|
||||
empty_block->ClearAllInstructions();
|
||||
}
|
||||
|
||||
changed = true;
|
||||
|
||||
if (FLAG_trace_constant_propagation) {
|
||||
OS::Print("Eliminated branch in B%"Pd" common target B%"Pd"\n",
|
||||
block->block_id(), join->block_id());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (changed) {
|
||||
graph_->DiscoverBlocks();
|
||||
// TODO(fschneider): Update dominator tree in place instead of recomputing.
|
||||
GrowableArray<BitVector*> dominance_frontier;
|
||||
graph_->ComputeDominators(&dominance_frontier);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ConstantPropagator::Transform() {
|
||||
if (FLAG_trace_constant_propagation) {
|
||||
OS::Print("\n==== Before constant propagation ====\n");
|
||||
|
@ -8468,24 +8548,16 @@ void ConstantPropagator::Transform() {
|
|||
!b.Done();
|
||||
b.Advance()) {
|
||||
BlockEntryInstr* block = b.Current();
|
||||
JoinEntryInstr* join = block->AsJoinEntry();
|
||||
if (!reachable_->Contains(block->preorder_number())) {
|
||||
if (FLAG_trace_constant_propagation) {
|
||||
OS::Print("Unreachable B%" Pd "\n", block->block_id());
|
||||
}
|
||||
// Remove all uses in unreachable blocks.
|
||||
if (join != NULL) {
|
||||
for (PhiIterator it(join); !it.Done(); it.Advance()) {
|
||||
it.Current()->UnuseAllInputs();
|
||||
}
|
||||
}
|
||||
block->UnuseAllInputs();
|
||||
for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) {
|
||||
it.Current()->UnuseAllInputs();
|
||||
}
|
||||
block->ClearAllInstructions();
|
||||
continue;
|
||||
}
|
||||
|
||||
JoinEntryInstr* join = block->AsJoinEntry();
|
||||
if (join != NULL) {
|
||||
// Remove phi inputs corresponding to unreachable predecessor blocks.
|
||||
// Predecessors will be recomputed (in block id order) after removing
|
||||
|
|
|
@ -297,8 +297,16 @@ class ConstantPropagator : public FlowGraphVisitor {
|
|||
|
||||
static void Optimize(FlowGraph* graph);
|
||||
|
||||
// Only visit branches to optimize away unreachable blocks discovered
|
||||
// by range analysis.
|
||||
// (1) Visit branches to optimize away unreachable blocks discovered by range
|
||||
// analysis.
|
||||
// (2) Eliminate branches that have the same true- and false-target: For
|
||||
// example, this occurs after expressions like
|
||||
//
|
||||
// if (a == null || b == null) {
|
||||
// ...
|
||||
// }
|
||||
//
|
||||
// where b is known to be null.
|
||||
static void OptimizeBranches(FlowGraph* graph);
|
||||
|
||||
// Used to initialize the abstract value of definitions.
|
||||
|
@ -308,6 +316,7 @@ class ConstantPropagator : public FlowGraphVisitor {
|
|||
void Analyze();
|
||||
void VisitBranches();
|
||||
void Transform();
|
||||
void EliminateRedundantBranches();
|
||||
|
||||
void SetReachable(BlockEntryInstr* block);
|
||||
void SetValue(Definition* definition, const Object& value);
|
||||
|
|
|
@ -1068,6 +1068,22 @@ void BlockEntryInstr::ReplaceAsPredecessorWith(BlockEntryInstr* new_block) {
|
|||
}
|
||||
|
||||
|
||||
void BlockEntryInstr::ClearAllInstructions() {
|
||||
JoinEntryInstr* join = this->AsJoinEntry();
|
||||
if (join != NULL) {
|
||||
for (PhiIterator it(join); !it.Done(); it.Advance()) {
|
||||
it.Current()->UnuseAllInputs();
|
||||
}
|
||||
}
|
||||
UnuseAllInputs();
|
||||
for (ForwardInstructionIterator it(this);
|
||||
!it.Done();
|
||||
it.Advance()) {
|
||||
it.Current()->UnuseAllInputs();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void JoinEntryInstr::InsertPhi(intptr_t var_index, intptr_t var_count) {
|
||||
// Lazily initialize the array of phis.
|
||||
// Currently, phis are stored in a sparse array that holds the phi
|
||||
|
|
|
@ -1386,6 +1386,10 @@ class BlockEntryInstr : public Instruction {
|
|||
|
||||
void set_block_id(intptr_t block_id) { block_id_ = block_id; }
|
||||
|
||||
// For all instruction in this block: Remove all inputs (including in the
|
||||
// environment) from their definition's use lists for all instructions.
|
||||
void ClearAllInstructions();
|
||||
|
||||
protected:
|
||||
BlockEntryInstr(intptr_t block_id, intptr_t try_index)
|
||||
: block_id_(block_id),
|
||||
|
|
Loading…
Reference in a new issue