Index: runtime/vm/flow_graph_optimizer.cc |
diff --git a/runtime/vm/flow_graph_optimizer.cc b/runtime/vm/flow_graph_optimizer.cc |
index 0c87164d50cdb1757452f06e8ede26be4f110d52..a495bcc929583cb769ba77bd0cdd0ec98eabdd0d 100644 |
--- a/runtime/vm/flow_graph_optimizer.cc |
+++ b/runtime/vm/flow_graph_optimizer.cc |
@@ -3930,6 +3930,39 @@ void ConstantPropagator::VisitStoreLocal(StoreLocalInstr* instr) { |
} |
+void ConstantPropagator::VisitIfThenElse(IfThenElseInstr* instr) { |
+ ASSERT(Token::IsEqualityOperator(instr->kind())); |
+ |
+ const Object& left = instr->left()->definition()->constant_value(); |
+ const Object& right = instr->right()->definition()->constant_value(); |
+ |
+ if (IsNonConstant(left) || IsNonConstant(right)) { |
+ // TODO(vegorov): incorporate nullability information into the lattice. |
+ if ((left.IsNull() && instr->right()->Type()->HasDecidableNullability()) || |
+ (right.IsNull() && instr->left()->Type()->HasDecidableNullability())) { |
+ bool result = left.IsNull() ? instr->right()->Type()->IsNull() |
+ : instr->left()->Type()->IsNull(); |
+ if (instr->kind() == Token::kNE_STRICT || |
+ instr->kind() == Token::kNE) { |
+ result = !result; |
+ } |
+ SetValue(instr, Smi::Handle( |
+ Smi::New(result ? instr->if_true() : instr->if_false()))); |
+ } else { |
+ SetValue(instr, non_constant_); |
+ } |
+ } else if (IsConstant(left) && IsConstant(right)) { |
+ bool result = (left.raw() == right.raw()); |
srdjan
2013/04/10 21:47:30
Since equality can be overridden, I do not think t
Vyacheslav Egorov (Google)
2013/04/10 21:51:28
Agreed, this is fragile.
The plan is to remove t
|
+ if (instr->kind() == Token::kNE_STRICT || |
+ instr->kind() == Token::kNE) { |
+ result = !result; |
+ } |
+ SetValue(instr, Smi::Handle( |
+ Smi::New(result ? instr->if_true() : instr->if_false()))); |
+ } |
+} |
+ |
+ |
void ConstantPropagator::VisitStrictCompare(StrictCompareInstr* instr) { |
const Object& left = instr->left()->definition()->constant_value(); |
const Object& right = instr->right()->definition()->constant_value(); |
@@ -4749,4 +4782,133 @@ void BranchSimplifier::Simplify(FlowGraph* flow_graph) { |
} |
+static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) { |
+ return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) && |
+ ((block->next() == block->last_instruction()) || |
+ ((block->next() == defn) && (defn->next() == block->last_instruction()))); |
+} |
+ |
+ |
+static void EliminateTrivialBlock(BlockEntryInstr* block, |
+ Definition* instr, |
+ IfThenElseInstr* before) { |
+ block->UnuseAllInputs(); |
+ block->last_instruction()->UnuseAllInputs(); |
+ |
+ if ((block->next() == instr) && |
+ (instr->next() == block->last_instruction())) { |
+ before->previous()->LinkTo(instr); |
+ instr->LinkTo(before); |
+ } |
+} |
+ |
+ |
+void IfConverter::Simplify(FlowGraph* flow_graph) { |
+ if (!IfThenElseInstr::IsSupported()) { |
+ return; |
+ } |
+ |
+ bool changed = false; |
+ |
+ const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder(); |
+ for (BlockIterator it(postorder); !it.Done(); it.Advance()) { |
+ BlockEntryInstr* block = it.Current(); |
+ JoinEntryInstr* join = block->AsJoinEntry(); |
+ |
+ // Detect diamond control flow pattern which materializes a value depending |
+ // on the result of the comparison: |
+ // |
+ // B_pred: |
+ // ... |
+ // Branch if COMP goto (B_pred1, B_pred2) |
+ // B_pred1: -- trivial block that contains at most one definition |
+ // v1 = Constant(...) |
+ // goto B_block |
+ // B_pred2: -- trivial block that contains at most one definition |
+ // v2 = Constant(...) |
+ // goto B_block |
+ // B_block: |
+ // v3 = phi(v1, v2) -- single phi |
+ // |
+ // and replace it with |
+ // |
+ // Ba: |
+ // v3 = IfThenElse(COMP ? v1 : v2) |
+ // |
+ if ((join != NULL) && |
+ (join->phis() != NULL) && |
+ (join->phis()->length() == 1) && |
+ (block->PredecessorCount() == 2)) { |
+ BlockEntryInstr* pred1 = block->PredecessorAt(0); |
+ BlockEntryInstr* pred2 = block->PredecessorAt(1); |
+ |
+ PhiInstr* phi = (*join->phis())[0]; |
+ Value* v1 = phi->InputAt(0); |
+ Value* v2 = phi->InputAt(1); |
+ |
+ if (IsTrivialBlock(pred1, v1->definition()) && |
+ IsTrivialBlock(pred2, v2->definition()) && |
+ (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) { |
+ BlockEntryInstr* pred = pred1->PredecessorAt(0); |
+ BranchInstr* branch = pred->last_instruction()->AsBranch(); |
+ ComparisonInstr* comparison = branch->comparison(); |
+ |
+ // Check if the platform supports efficient branchless IfThenElseInstr |
+ // for the given combination of comparison and values flowing from |
+ // false and true paths. |
+ if (IfThenElseInstr::Supports(comparison, v1, v2)) { |
+ Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2; |
+ Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2; |
+ |
+ IfThenElseInstr* if_then_else = new IfThenElseInstr( |
+ comparison->kind(), |
+ comparison->InputAt(0)->Copy(), |
+ comparison->InputAt(1)->Copy(), |
+ if_true->Copy(), |
+ if_false->Copy()); |
+ flow_graph->InsertBefore(branch, |
+ if_then_else, |
+ NULL, |
+ Definition::kValue); |
+ |
+ phi->ReplaceUsesWith(if_then_else); |
+ |
+ // Connect IfThenElseInstr to the first instruction in the merge block |
+ // effectively eliminating diamond control flow. |
+ // Current block as well as pred1 and pred2 blocks are no longer in |
+ // the graph at this point. |
+ if_then_else->LinkTo(join->next()); |
+ pred->set_last_instruction(join->last_instruction()); |
+ |
+ // Resulting block must inherit block id from the eliminated current |
+ // block to guarantee that ordering of phi operands in its successor |
+ // stays consistent. |
+ pred->set_block_id(block->block_id()); |
+ |
+ // If v1 and v2 were defined inside eliminated blocks pred1/pred2 |
+ // move them out to the place before inserted IfThenElse instruction. |
+ EliminateTrivialBlock(pred1, v1->definition(), if_then_else); |
+ EliminateTrivialBlock(pred2, v2->definition(), if_then_else); |
+ |
+ // Update use lists to reflect changes in the graph. |
+ phi->UnuseAllInputs(); |
+ branch->UnuseAllInputs(); |
+ |
+ // The graph has changed. Recompute dominators and block orders after |
+ // this pass is finished. |
+ changed = true; |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (changed) { |
+ // We may have changed the block order and the dominator tree. |
+ flow_graph->DiscoverBlocks(); |
+ GrowableArray<BitVector*> dominance_frontier; |
+ flow_graph->ComputeDominators(&dominance_frontier); |
+ } |
+} |
+ |
+ |
} // namespace dart |