| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/flow_graph_optimizer.h" | 5 #include "vm/flow_graph_optimizer.h" |
| 6 | 6 |
| 7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
| 8 #include "vm/branch_optimizer.h" |
| 8 #include "vm/cha.h" | 9 #include "vm/cha.h" |
| 9 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
| 10 #include "vm/cpu.h" | 11 #include "vm/cpu.h" |
| 11 #include "vm/dart_entry.h" | 12 #include "vm/dart_entry.h" |
| 12 #include "vm/exceptions.h" | 13 #include "vm/exceptions.h" |
| 13 #include "vm/flow_graph_builder.h" | 14 #include "vm/flow_graph_builder.h" |
| 14 #include "vm/flow_graph_compiler.h" | 15 #include "vm/flow_graph_compiler.h" |
| 15 #include "vm/flow_graph_range_analysis.h" | 16 #include "vm/flow_graph_range_analysis.h" |
| 16 #include "vm/hash_map.h" | 17 #include "vm/hash_map.h" |
| 17 #include "vm/il_printer.h" | 18 #include "vm/il_printer.h" |
| (...skipping 7881 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 7899 changed = OptimizeRecursive(graph, child, &child_map) || changed; | 7900 changed = OptimizeRecursive(graph, child, &child_map) || changed; |
| 7900 } else { | 7901 } else { |
| 7901 // Reuse map for the last child. | 7902 // Reuse map for the last child. |
| 7902 changed = OptimizeRecursive(graph, child, map) || changed; | 7903 changed = OptimizeRecursive(graph, child, map) || changed; |
| 7903 } | 7904 } |
| 7904 } | 7905 } |
| 7905 return changed; | 7906 return changed; |
| 7906 } | 7907 } |
| 7907 | 7908 |
| 7908 | 7909 |
| 7909 // Returns true if the given phi has a single input use and | |
| 7910 // is used in the environments either at the corresponding block entry or | |
| 7911 // at the same instruction where input use is. | |
| 7912 static bool PhiHasSingleUse(PhiInstr* phi, Value* use) { | |
| 7913 if ((use->next_use() != NULL) || (phi->input_use_list() != use)) { | |
| 7914 return false; | |
| 7915 } | |
| 7916 | |
| 7917 BlockEntryInstr* block = phi->block(); | |
| 7918 for (Value* env_use = phi->env_use_list(); | |
| 7919 env_use != NULL; | |
| 7920 env_use = env_use->next_use()) { | |
| 7921 if ((env_use->instruction() != block) && | |
| 7922 (env_use->instruction() != use->instruction())) { | |
| 7923 return false; | |
| 7924 } | |
| 7925 } | |
| 7926 | |
| 7927 return true; | |
| 7928 } | |
| 7929 | |
| 7930 | |
| 7931 bool BranchSimplifier::Match(JoinEntryInstr* block) { | |
| 7932 // Match the pattern of a branch on a comparison whose left operand is a | |
| 7933 // phi from the same block, and whose right operand is a constant. | |
| 7934 // | |
| 7935 // Branch(Comparison(kind, Phi, Constant)) | |
| 7936 // | |
| 7937 // These are the branches produced by inlining in a test context. Also, | |
| 7938 // the phi has no other uses so they can simply be eliminated. The block | |
| 7939 // has no other phis and no instructions intervening between the phi and | |
| 7940 // branch so the block can simply be eliminated. | |
| 7941 BranchInstr* branch = block->last_instruction()->AsBranch(); | |
| 7942 ASSERT(branch != NULL); | |
| 7943 ComparisonInstr* comparison = branch->comparison(); | |
| 7944 Value* left = comparison->left(); | |
| 7945 PhiInstr* phi = left->definition()->AsPhi(); | |
| 7946 Value* right = comparison->right(); | |
| 7947 ConstantInstr* constant = | |
| 7948 (right == NULL) ? NULL : right->definition()->AsConstant(); | |
| 7949 return (phi != NULL) && | |
| 7950 (constant != NULL) && | |
| 7951 (phi->GetBlock() == block) && | |
| 7952 PhiHasSingleUse(phi, left) && | |
| 7953 (block->next() == branch) && | |
| 7954 (block->phis()->length() == 1); | |
| 7955 } | |
| 7956 | |
| 7957 | |
| 7958 JoinEntryInstr* BranchSimplifier::ToJoinEntry(Zone* zone, | |
| 7959 TargetEntryInstr* target) { | |
| 7960 // Convert a target block into a join block. Branches will be duplicated | |
| 7961 // so the former true and false targets become joins of the control flows | |
| 7962 // from all the duplicated branches. | |
| 7963 JoinEntryInstr* join = | |
| 7964 new(zone) JoinEntryInstr(target->block_id(), target->try_index()); | |
| 7965 join->InheritDeoptTarget(zone, target); | |
| 7966 join->LinkTo(target->next()); | |
| 7967 join->set_last_instruction(target->last_instruction()); | |
| 7968 target->UnuseAllInputs(); | |
| 7969 return join; | |
| 7970 } | |
| 7971 | |
| 7972 | |
| 7973 BranchInstr* BranchSimplifier::CloneBranch(Zone* zone, | |
| 7974 BranchInstr* branch, | |
| 7975 Value* new_left, | |
| 7976 Value* new_right) { | |
| 7977 ComparisonInstr* comparison = branch->comparison(); | |
| 7978 ComparisonInstr* new_comparison = | |
| 7979 comparison->CopyWithNewOperands(new_left, new_right); | |
| 7980 BranchInstr* new_branch = new(zone) BranchInstr(new_comparison); | |
| 7981 new_branch->set_is_checked(branch->is_checked()); | |
| 7982 return new_branch; | |
| 7983 } | |
| 7984 | |
| 7985 | |
| 7986 void BranchSimplifier::Simplify(FlowGraph* flow_graph) { | |
| 7987 // Optimize some branches that test the value of a phi. When it is safe | |
| 7988 // to do so, push the branch to each of the predecessor blocks. This is | |
| 7989 // an optimization when (a) it can avoid materializing a boolean object at | |
| 7990 // the phi only to test its value, and (b) it can expose opportunities for | |
| 7991 // constant propagation and unreachable code elimination. This | |
| 7992 // optimization is intended to run after inlining which creates | |
| 7993 // opportunities for optimization (a) and before constant folding which | |
| 7994 // can perform optimization (b). | |
| 7995 | |
| 7996 // Begin with a worklist of join blocks ending in branches. They are | |
| 7997 // candidates for the pattern below. | |
| 7998 Zone* zone = flow_graph->zone(); | |
| 7999 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder(); | |
| 8000 GrowableArray<BlockEntryInstr*> worklist(postorder.length()); | |
| 8001 for (BlockIterator it(postorder); !it.Done(); it.Advance()) { | |
| 8002 BlockEntryInstr* block = it.Current(); | |
| 8003 if (block->IsJoinEntry() && block->last_instruction()->IsBranch()) { | |
| 8004 worklist.Add(block); | |
| 8005 } | |
| 8006 } | |
| 8007 | |
| 8008 // Rewrite until no more instance of the pattern exists. | |
| 8009 bool changed = false; | |
| 8010 while (!worklist.is_empty()) { | |
| 8011 // All blocks in the worklist are join blocks (ending with a branch). | |
| 8012 JoinEntryInstr* block = worklist.RemoveLast()->AsJoinEntry(); | |
| 8013 ASSERT(block != NULL); | |
| 8014 | |
| 8015 if (Match(block)) { | |
| 8016 changed = true; | |
| 8017 | |
| 8018 // The branch will be copied and pushed to all the join's | |
| 8019 // predecessors. Convert the true and false target blocks into join | |
| 8020 // blocks to join the control flows from all of the true | |
| 8021 // (respectively, false) targets of the copied branches. | |
| 8022 // | |
| 8023 // The converted join block will have no phis, so it cannot be another | |
| 8024 // instance of the pattern. There is thus no need to add it to the | |
| 8025 // worklist. | |
| 8026 BranchInstr* branch = block->last_instruction()->AsBranch(); | |
| 8027 ASSERT(branch != NULL); | |
| 8028 JoinEntryInstr* join_true = | |
| 8029 ToJoinEntry(zone, branch->true_successor()); | |
| 8030 JoinEntryInstr* join_false = | |
| 8031 ToJoinEntry(zone, branch->false_successor()); | |
| 8032 | |
| 8033 ComparisonInstr* comparison = branch->comparison(); | |
| 8034 PhiInstr* phi = comparison->left()->definition()->AsPhi(); | |
| 8035 ConstantInstr* constant = comparison->right()->definition()->AsConstant(); | |
| 8036 ASSERT(constant != NULL); | |
| 8037 // Copy the constant and branch and push it to all the predecessors. | |
| 8038 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) { | |
| 8039 GotoInstr* old_goto = | |
| 8040 block->PredecessorAt(i)->last_instruction()->AsGoto(); | |
| 8041 ASSERT(old_goto != NULL); | |
| 8042 | |
| 8043 // Replace the goto in each predecessor with a rewritten branch, | |
| 8044 // rewritten to use the corresponding phi input instead of the phi. | |
| 8045 Value* new_left = phi->InputAt(i)->Copy(zone); | |
| 8046 Value* new_right = new(zone) Value(constant); | |
| 8047 BranchInstr* new_branch = | |
| 8048 CloneBranch(zone, branch, new_left, new_right); | |
| 8049 if (branch->env() == NULL) { | |
| 8050 new_branch->InheritDeoptTarget(zone, old_goto); | |
| 8051 } else { | |
| 8052 // Take the environment from the branch if it has one. | |
| 8053 new_branch->InheritDeoptTarget(zone, branch); | |
| 8054 // InheritDeoptTarget gave the new branch's comparison the same | |
| 8055 // deopt id that it gave the new branch. The id should be the | |
| 8056 // deopt id of the original comparison. | |
| 8057 new_branch->comparison()->SetDeoptId(*comparison); | |
| 8058 // The phi can be used in the branch's environment. Rename such | |
| 8059 // uses. | |
| 8060 for (Environment::DeepIterator it(new_branch->env()); | |
| 8061 !it.Done(); | |
| 8062 it.Advance()) { | |
| 8063 Value* use = it.CurrentValue(); | |
| 8064 if (use->definition() == phi) { | |
| 8065 Definition* replacement = phi->InputAt(i)->definition(); | |
| 8066 use->RemoveFromUseList(); | |
| 8067 use->set_definition(replacement); | |
| 8068 replacement->AddEnvUse(use); | |
| 8069 } | |
| 8070 } | |
| 8071 } | |
| 8072 | |
| 8073 new_branch->InsertBefore(old_goto); | |
| 8074 new_branch->set_next(NULL); // Detaching the goto from the graph. | |
| 8075 old_goto->UnuseAllInputs(); | |
| 8076 | |
| 8077 // Update the predecessor block. We may have created another | |
| 8078 // instance of the pattern so add it to the worklist if necessary. | |
| 8079 BlockEntryInstr* branch_block = new_branch->GetBlock(); | |
| 8080 branch_block->set_last_instruction(new_branch); | |
| 8081 if (branch_block->IsJoinEntry()) worklist.Add(branch_block); | |
| 8082 | |
| 8083 // Connect the branch to the true and false joins, via empty target | |
| 8084 // blocks. | |
| 8085 TargetEntryInstr* true_target = | |
| 8086 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 1, | |
| 8087 block->try_index()); | |
| 8088 true_target->InheritDeoptTarget(zone, join_true); | |
| 8089 TargetEntryInstr* false_target = | |
| 8090 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 2, | |
| 8091 block->try_index()); | |
| 8092 false_target->InheritDeoptTarget(zone, join_false); | |
| 8093 flow_graph->set_max_block_id(flow_graph->max_block_id() + 2); | |
| 8094 *new_branch->true_successor_address() = true_target; | |
| 8095 *new_branch->false_successor_address() = false_target; | |
| 8096 GotoInstr* goto_true = new(zone) GotoInstr(join_true); | |
| 8097 goto_true->InheritDeoptTarget(zone, join_true); | |
| 8098 true_target->LinkTo(goto_true); | |
| 8099 true_target->set_last_instruction(goto_true); | |
| 8100 GotoInstr* goto_false = new(zone) GotoInstr(join_false); | |
| 8101 goto_false->InheritDeoptTarget(zone, join_false); | |
| 8102 false_target->LinkTo(goto_false); | |
| 8103 false_target->set_last_instruction(goto_false); | |
| 8104 } | |
| 8105 // When all predecessors have been rewritten, the original block is | |
| 8106 // unreachable from the graph. | |
| 8107 phi->UnuseAllInputs(); | |
| 8108 branch->UnuseAllInputs(); | |
| 8109 block->UnuseAllInputs(); | |
| 8110 ASSERT(!phi->HasUses()); | |
| 8111 } | |
| 8112 } | |
| 8113 | |
| 8114 if (changed) { | |
| 8115 // We may have changed the block order and the dominator tree. | |
| 8116 flow_graph->DiscoverBlocks(); | |
| 8117 GrowableArray<BitVector*> dominance_frontier; | |
| 8118 flow_graph->ComputeDominators(&dominance_frontier); | |
| 8119 } | |
| 8120 } | |
| 8121 | |
| 8122 | |
| 8123 static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) { | |
| 8124 return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) && | |
| 8125 ((block->next() == block->last_instruction()) || | |
| 8126 ((block->next() == defn) && (defn->next() == block->last_instruction()))); | |
| 8127 } | |
| 8128 | |
| 8129 | |
| 8130 static void EliminateTrivialBlock(BlockEntryInstr* block, | |
| 8131 Definition* instr, | |
| 8132 IfThenElseInstr* before) { | |
| 8133 block->UnuseAllInputs(); | |
| 8134 block->last_instruction()->UnuseAllInputs(); | |
| 8135 | |
| 8136 if ((block->next() == instr) && | |
| 8137 (instr->next() == block->last_instruction())) { | |
| 8138 before->previous()->LinkTo(instr); | |
| 8139 instr->LinkTo(before); | |
| 8140 } | |
| 8141 } | |
| 8142 | |
| 8143 | |
| 8144 void IfConverter::Simplify(FlowGraph* flow_graph) { | |
| 8145 Zone* zone = flow_graph->zone(); | |
| 8146 bool changed = false; | |
| 8147 | |
| 8148 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder(); | |
| 8149 for (BlockIterator it(postorder); !it.Done(); it.Advance()) { | |
| 8150 BlockEntryInstr* block = it.Current(); | |
| 8151 JoinEntryInstr* join = block->AsJoinEntry(); | |
| 8152 | |
| 8153 // Detect diamond control flow pattern which materializes a value depending | |
| 8154 // on the result of the comparison: | |
| 8155 // | |
| 8156 // B_pred: | |
| 8157 // ... | |
| 8158 // Branch if COMP goto (B_pred1, B_pred2) | |
| 8159 // B_pred1: -- trivial block that contains at most one definition | |
| 8160 // v1 = Constant(...) | |
| 8161 // goto B_block | |
| 8162 // B_pred2: -- trivial block that contains at most one definition | |
| 8163 // v2 = Constant(...) | |
| 8164 // goto B_block | |
| 8165 // B_block: | |
| 8166 // v3 = phi(v1, v2) -- single phi | |
| 8167 // | |
| 8168 // and replace it with | |
| 8169 // | |
| 8170 // Ba: | |
| 8171 // v3 = IfThenElse(COMP ? v1 : v2) | |
| 8172 // | |
| 8173 if ((join != NULL) && | |
| 8174 (join->phis() != NULL) && | |
| 8175 (join->phis()->length() == 1) && | |
| 8176 (block->PredecessorCount() == 2)) { | |
| 8177 BlockEntryInstr* pred1 = block->PredecessorAt(0); | |
| 8178 BlockEntryInstr* pred2 = block->PredecessorAt(1); | |
| 8179 | |
| 8180 PhiInstr* phi = (*join->phis())[0]; | |
| 8181 Value* v1 = phi->InputAt(0); | |
| 8182 Value* v2 = phi->InputAt(1); | |
| 8183 | |
| 8184 if (IsTrivialBlock(pred1, v1->definition()) && | |
| 8185 IsTrivialBlock(pred2, v2->definition()) && | |
| 8186 (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) { | |
| 8187 BlockEntryInstr* pred = pred1->PredecessorAt(0); | |
| 8188 BranchInstr* branch = pred->last_instruction()->AsBranch(); | |
| 8189 ComparisonInstr* comparison = branch->comparison(); | |
| 8190 | |
| 8191 // Check if the platform supports efficient branchless IfThenElseInstr | |
| 8192 // for the given combination of comparison and values flowing from | |
| 8193 // false and true paths. | |
| 8194 if (IfThenElseInstr::Supports(comparison, v1, v2)) { | |
| 8195 Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2; | |
| 8196 Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2; | |
| 8197 | |
| 8198 ComparisonInstr* new_comparison = | |
| 8199 comparison->CopyWithNewOperands( | |
| 8200 comparison->left()->Copy(zone), | |
| 8201 comparison->right()->Copy(zone)); | |
| 8202 IfThenElseInstr* if_then_else = new(zone) IfThenElseInstr( | |
| 8203 new_comparison, | |
| 8204 if_true->Copy(zone), | |
| 8205 if_false->Copy(zone)); | |
| 8206 flow_graph->InsertBefore(branch, | |
| 8207 if_then_else, | |
| 8208 NULL, | |
| 8209 FlowGraph::kValue); | |
| 8210 | |
| 8211 phi->ReplaceUsesWith(if_then_else); | |
| 8212 | |
| 8213 // Connect IfThenElseInstr to the first instruction in the merge block | |
| 8214 // effectively eliminating diamond control flow. | |
| 8215 // Current block as well as pred1 and pred2 blocks are no longer in | |
| 8216 // the graph at this point. | |
| 8217 if_then_else->LinkTo(join->next()); | |
| 8218 pred->set_last_instruction(join->last_instruction()); | |
| 8219 | |
| 8220 // Resulting block must inherit block id from the eliminated current | |
| 8221 // block to guarantee that ordering of phi operands in its successor | |
| 8222 // stays consistent. | |
| 8223 pred->set_block_id(block->block_id()); | |
| 8224 | |
| 8225 // If v1 and v2 were defined inside eliminated blocks pred1/pred2 | |
| 8226 // move them out to the place before inserted IfThenElse instruction. | |
| 8227 EliminateTrivialBlock(pred1, v1->definition(), if_then_else); | |
| 8228 EliminateTrivialBlock(pred2, v2->definition(), if_then_else); | |
| 8229 | |
| 8230 // Update use lists to reflect changes in the graph. | |
| 8231 phi->UnuseAllInputs(); | |
| 8232 branch->UnuseAllInputs(); | |
| 8233 block->UnuseAllInputs(); | |
| 8234 | |
| 8235 // The graph has changed. Recompute dominators and block orders after | |
| 8236 // this pass is finished. | |
| 8237 changed = true; | |
| 8238 } | |
| 8239 } | |
| 8240 } | |
| 8241 } | |
| 8242 | |
| 8243 if (changed) { | |
| 8244 // We may have changed the block order and the dominator tree. | |
| 8245 flow_graph->DiscoverBlocks(); | |
| 8246 GrowableArray<BitVector*> dominance_frontier; | |
| 8247 flow_graph->ComputeDominators(&dominance_frontier); | |
| 8248 } | |
| 8249 } | |
| 8250 | |
| 8251 | |
| 8252 void FlowGraphOptimizer::EliminateEnvironments() { | 7910 void FlowGraphOptimizer::EliminateEnvironments() { |
| 8253 // After this pass we can no longer perform LICM and hoist instructions | 7911 // After this pass we can no longer perform LICM and hoist instructions |
| 8254 // that can deoptimize. | 7912 // that can deoptimize. |
| 8255 | 7913 |
| 8256 flow_graph_->disallow_licm(); | 7914 flow_graph_->disallow_licm(); |
| 8257 for (intptr_t i = 0; i < block_order_.length(); ++i) { | 7915 for (intptr_t i = 0; i < block_order_.length(); ++i) { |
| 8258 BlockEntryInstr* block = block_order_[i]; | 7916 BlockEntryInstr* block = block_order_[i]; |
| 8259 block->RemoveEnvironment(); | 7917 block->RemoveEnvironment(); |
| 8260 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { | 7918 for (ForwardInstructionIterator it(block); !it.Done(); it.Advance()) { |
| 8261 Instruction* current = it.Current(); | 7919 Instruction* current = it.Current(); |
| (...skipping 609 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 8871 | 8529 |
| 8872 // Insert materializations at environment uses. | 8530 // Insert materializations at environment uses. |
| 8873 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { | 8531 for (intptr_t i = 0; i < exits_collector_.exits().length(); i++) { |
| 8874 CreateMaterializationAt( | 8532 CreateMaterializationAt( |
| 8875 exits_collector_.exits()[i], alloc, *slots); | 8533 exits_collector_.exits()[i], alloc, *slots); |
| 8876 } | 8534 } |
| 8877 } | 8535 } |
| 8878 | 8536 |
| 8879 | 8537 |
| 8880 } // namespace dart | 8538 } // namespace dart |
| OLD | NEW |