Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(61)

Side by Side Diff: runtime/vm/flow_graph_optimizer.cc

Issue 1679833002: VM: Move branch optimizations into a separate file. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/vm_sources.gypi » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
OLDNEW
« no previous file with comments | « runtime/vm/flow_graph_optimizer.h ('k') | runtime/vm/vm_sources.gypi » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698