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:
fschneider@google.com 2014-05-01 23:17:24 +00:00
parent 333cd2ad67
commit 07fcee05c8
5 changed files with 115 additions and 13 deletions

View file

@ -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());
}

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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),