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

Side by Side Diff: runtime/vm/branch_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/branch_optimizer.h ('k') | runtime/vm/compiler.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file
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.
4
5 #include "vm/branch_optimizer.h"
6
7 #include "vm/flow_graph.h"
8 #include "vm/intermediate_language.h"
9
10 namespace dart {
11
12 // Returns true if the given phi has a single input use and
13 // is used in the environments either at the corresponding block entry or
14 // at the same instruction where input use is.
15 static bool PhiHasSingleUse(PhiInstr* phi, Value* use) {
16 if ((use->next_use() != NULL) || (phi->input_use_list() != use)) {
17 return false;
18 }
19
20 BlockEntryInstr* block = phi->block();
21 for (Value* env_use = phi->env_use_list();
22 env_use != NULL;
23 env_use = env_use->next_use()) {
24 if ((env_use->instruction() != block) &&
25 (env_use->instruction() != use->instruction())) {
26 return false;
27 }
28 }
29
30 return true;
31 }
32
33
34 bool BranchSimplifier::Match(JoinEntryInstr* block) {
35 // Match the pattern of a branch on a comparison whose left operand is a
36 // phi from the same block, and whose right operand is a constant.
37 //
38 // Branch(Comparison(kind, Phi, Constant))
39 //
40 // These are the branches produced by inlining in a test context. Also,
41 // the phi has no other uses so they can simply be eliminated. The block
42 // has no other phis and no instructions intervening between the phi and
43 // branch so the block can simply be eliminated.
44 BranchInstr* branch = block->last_instruction()->AsBranch();
45 ASSERT(branch != NULL);
46 ComparisonInstr* comparison = branch->comparison();
47 Value* left = comparison->left();
48 PhiInstr* phi = left->definition()->AsPhi();
49 Value* right = comparison->right();
50 ConstantInstr* constant =
51 (right == NULL) ? NULL : right->definition()->AsConstant();
52 return (phi != NULL) &&
53 (constant != NULL) &&
54 (phi->GetBlock() == block) &&
55 PhiHasSingleUse(phi, left) &&
56 (block->next() == branch) &&
57 (block->phis()->length() == 1);
58 }
59
60
61 JoinEntryInstr* BranchSimplifier::ToJoinEntry(Zone* zone,
62 TargetEntryInstr* target) {
63 // Convert a target block into a join block. Branches will be duplicated
64 // so the former true and false targets become joins of the control flows
65 // from all the duplicated branches.
66 JoinEntryInstr* join =
67 new(zone) JoinEntryInstr(target->block_id(), target->try_index());
68 join->InheritDeoptTarget(zone, target);
69 join->LinkTo(target->next());
70 join->set_last_instruction(target->last_instruction());
71 target->UnuseAllInputs();
72 return join;
73 }
74
75
76 BranchInstr* BranchSimplifier::CloneBranch(Zone* zone,
77 BranchInstr* branch,
78 Value* new_left,
79 Value* new_right) {
80 ComparisonInstr* comparison = branch->comparison();
81 ComparisonInstr* new_comparison =
82 comparison->CopyWithNewOperands(new_left, new_right);
83 BranchInstr* new_branch = new(zone) BranchInstr(new_comparison);
84 new_branch->set_is_checked(branch->is_checked());
85 return new_branch;
86 }
87
88
89 void BranchSimplifier::Simplify(FlowGraph* flow_graph) {
90 // Optimize some branches that test the value of a phi. When it is safe
91 // to do so, push the branch to each of the predecessor blocks. This is
92 // an optimization when (a) it can avoid materializing a boolean object at
93 // the phi only to test its value, and (b) it can expose opportunities for
94 // constant propagation and unreachable code elimination. This
95 // optimization is intended to run after inlining which creates
96 // opportunities for optimization (a) and before constant folding which
97 // can perform optimization (b).
98
99 // Begin with a worklist of join blocks ending in branches. They are
100 // candidates for the pattern below.
101 Zone* zone = flow_graph->zone();
102 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
103 GrowableArray<BlockEntryInstr*> worklist(postorder.length());
104 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
105 BlockEntryInstr* block = it.Current();
106 if (block->IsJoinEntry() && block->last_instruction()->IsBranch()) {
107 worklist.Add(block);
108 }
109 }
110
111 // Rewrite until no more instance of the pattern exists.
112 bool changed = false;
113 while (!worklist.is_empty()) {
114 // All blocks in the worklist are join blocks (ending with a branch).
115 JoinEntryInstr* block = worklist.RemoveLast()->AsJoinEntry();
116 ASSERT(block != NULL);
117
118 if (Match(block)) {
119 changed = true;
120
121 // The branch will be copied and pushed to all the join's
122 // predecessors. Convert the true and false target blocks into join
123 // blocks to join the control flows from all of the true
124 // (respectively, false) targets of the copied branches.
125 //
126 // The converted join block will have no phis, so it cannot be another
127 // instance of the pattern. There is thus no need to add it to the
128 // worklist.
129 BranchInstr* branch = block->last_instruction()->AsBranch();
130 ASSERT(branch != NULL);
131 JoinEntryInstr* join_true =
132 ToJoinEntry(zone, branch->true_successor());
133 JoinEntryInstr* join_false =
134 ToJoinEntry(zone, branch->false_successor());
135
136 ComparisonInstr* comparison = branch->comparison();
137 PhiInstr* phi = comparison->left()->definition()->AsPhi();
138 ConstantInstr* constant = comparison->right()->definition()->AsConstant();
139 ASSERT(constant != NULL);
140 // Copy the constant and branch and push it to all the predecessors.
141 for (intptr_t i = 0, count = block->PredecessorCount(); i < count; ++i) {
142 GotoInstr* old_goto =
143 block->PredecessorAt(i)->last_instruction()->AsGoto();
144 ASSERT(old_goto != NULL);
145
146 // Replace the goto in each predecessor with a rewritten branch,
147 // rewritten to use the corresponding phi input instead of the phi.
148 Value* new_left = phi->InputAt(i)->Copy(zone);
149 Value* new_right = new(zone) Value(constant);
150 BranchInstr* new_branch =
151 CloneBranch(zone, branch, new_left, new_right);
152 if (branch->env() == NULL) {
153 new_branch->InheritDeoptTarget(zone, old_goto);
154 } else {
155 // Take the environment from the branch if it has one.
156 new_branch->InheritDeoptTarget(zone, branch);
157 // InheritDeoptTarget gave the new branch's comparison the same
158 // deopt id that it gave the new branch. The id should be the
159 // deopt id of the original comparison.
160 new_branch->comparison()->SetDeoptId(*comparison);
161 // The phi can be used in the branch's environment. Rename such
162 // uses.
163 for (Environment::DeepIterator it(new_branch->env());
164 !it.Done();
165 it.Advance()) {
166 Value* use = it.CurrentValue();
167 if (use->definition() == phi) {
168 Definition* replacement = phi->InputAt(i)->definition();
169 use->RemoveFromUseList();
170 use->set_definition(replacement);
171 replacement->AddEnvUse(use);
172 }
173 }
174 }
175
176 new_branch->InsertBefore(old_goto);
177 new_branch->set_next(NULL); // Detaching the goto from the graph.
178 old_goto->UnuseAllInputs();
179
180 // Update the predecessor block. We may have created another
181 // instance of the pattern so add it to the worklist if necessary.
182 BlockEntryInstr* branch_block = new_branch->GetBlock();
183 branch_block->set_last_instruction(new_branch);
184 if (branch_block->IsJoinEntry()) worklist.Add(branch_block);
185
186 // Connect the branch to the true and false joins, via empty target
187 // blocks.
188 TargetEntryInstr* true_target =
189 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 1,
190 block->try_index());
191 true_target->InheritDeoptTarget(zone, join_true);
192 TargetEntryInstr* false_target =
193 new(zone) TargetEntryInstr(flow_graph->max_block_id() + 2,
194 block->try_index());
195 false_target->InheritDeoptTarget(zone, join_false);
196 flow_graph->set_max_block_id(flow_graph->max_block_id() + 2);
197 *new_branch->true_successor_address() = true_target;
198 *new_branch->false_successor_address() = false_target;
199 GotoInstr* goto_true = new(zone) GotoInstr(join_true);
200 goto_true->InheritDeoptTarget(zone, join_true);
201 true_target->LinkTo(goto_true);
202 true_target->set_last_instruction(goto_true);
203 GotoInstr* goto_false = new(zone) GotoInstr(join_false);
204 goto_false->InheritDeoptTarget(zone, join_false);
205 false_target->LinkTo(goto_false);
206 false_target->set_last_instruction(goto_false);
207 }
208 // When all predecessors have been rewritten, the original block is
209 // unreachable from the graph.
210 phi->UnuseAllInputs();
211 branch->UnuseAllInputs();
212 block->UnuseAllInputs();
213 ASSERT(!phi->HasUses());
214 }
215 }
216
217 if (changed) {
218 // We may have changed the block order and the dominator tree.
219 flow_graph->DiscoverBlocks();
220 GrowableArray<BitVector*> dominance_frontier;
221 flow_graph->ComputeDominators(&dominance_frontier);
222 }
223 }
224
225
226 static bool IsTrivialBlock(BlockEntryInstr* block, Definition* defn) {
227 return (block->IsTargetEntry() && (block->PredecessorCount() == 1)) &&
228 ((block->next() == block->last_instruction()) ||
229 ((block->next() == defn) && (defn->next() == block->last_instruction())));
230 }
231
232
233 static void EliminateTrivialBlock(BlockEntryInstr* block,
234 Definition* instr,
235 IfThenElseInstr* before) {
236 block->UnuseAllInputs();
237 block->last_instruction()->UnuseAllInputs();
238
239 if ((block->next() == instr) &&
240 (instr->next() == block->last_instruction())) {
241 before->previous()->LinkTo(instr);
242 instr->LinkTo(before);
243 }
244 }
245
246
247 void IfConverter::Simplify(FlowGraph* flow_graph) {
248 Zone* zone = flow_graph->zone();
249 bool changed = false;
250
251 const GrowableArray<BlockEntryInstr*>& postorder = flow_graph->postorder();
252 for (BlockIterator it(postorder); !it.Done(); it.Advance()) {
253 BlockEntryInstr* block = it.Current();
254 JoinEntryInstr* join = block->AsJoinEntry();
255
256 // Detect diamond control flow pattern which materializes a value depending
257 // on the result of the comparison:
258 //
259 // B_pred:
260 // ...
261 // Branch if COMP goto (B_pred1, B_pred2)
262 // B_pred1: -- trivial block that contains at most one definition
263 // v1 = Constant(...)
264 // goto B_block
265 // B_pred2: -- trivial block that contains at most one definition
266 // v2 = Constant(...)
267 // goto B_block
268 // B_block:
269 // v3 = phi(v1, v2) -- single phi
270 //
271 // and replace it with
272 //
273 // Ba:
274 // v3 = IfThenElse(COMP ? v1 : v2)
275 //
276 if ((join != NULL) &&
277 (join->phis() != NULL) &&
278 (join->phis()->length() == 1) &&
279 (block->PredecessorCount() == 2)) {
280 BlockEntryInstr* pred1 = block->PredecessorAt(0);
281 BlockEntryInstr* pred2 = block->PredecessorAt(1);
282
283 PhiInstr* phi = (*join->phis())[0];
284 Value* v1 = phi->InputAt(0);
285 Value* v2 = phi->InputAt(1);
286
287 if (IsTrivialBlock(pred1, v1->definition()) &&
288 IsTrivialBlock(pred2, v2->definition()) &&
289 (pred1->PredecessorAt(0) == pred2->PredecessorAt(0))) {
290 BlockEntryInstr* pred = pred1->PredecessorAt(0);
291 BranchInstr* branch = pred->last_instruction()->AsBranch();
292 ComparisonInstr* comparison = branch->comparison();
293
294 // Check if the platform supports efficient branchless IfThenElseInstr
295 // for the given combination of comparison and values flowing from
296 // false and true paths.
297 if (IfThenElseInstr::Supports(comparison, v1, v2)) {
298 Value* if_true = (pred1 == branch->true_successor()) ? v1 : v2;
299 Value* if_false = (pred2 == branch->true_successor()) ? v1 : v2;
300
301 ComparisonInstr* new_comparison =
302 comparison->CopyWithNewOperands(
303 comparison->left()->Copy(zone),
304 comparison->right()->Copy(zone));
305 IfThenElseInstr* if_then_else = new(zone) IfThenElseInstr(
306 new_comparison,
307 if_true->Copy(zone),
308 if_false->Copy(zone));
309 flow_graph->InsertBefore(branch,
310 if_then_else,
311 NULL,
312 FlowGraph::kValue);
313
314 phi->ReplaceUsesWith(if_then_else);
315
316 // Connect IfThenElseInstr to the first instruction in the merge block
317 // effectively eliminating diamond control flow.
318 // Current block as well as pred1 and pred2 blocks are no longer in
319 // the graph at this point.
320 if_then_else->LinkTo(join->next());
321 pred->set_last_instruction(join->last_instruction());
322
323 // Resulting block must inherit block id from the eliminated current
324 // block to guarantee that ordering of phi operands in its successor
325 // stays consistent.
326 pred->set_block_id(block->block_id());
327
328 // If v1 and v2 were defined inside eliminated blocks pred1/pred2
329 // move them out to the place before inserted IfThenElse instruction.
330 EliminateTrivialBlock(pred1, v1->definition(), if_then_else);
331 EliminateTrivialBlock(pred2, v2->definition(), if_then_else);
332
333 // Update use lists to reflect changes in the graph.
334 phi->UnuseAllInputs();
335 branch->UnuseAllInputs();
336 block->UnuseAllInputs();
337
338 // The graph has changed. Recompute dominators and block orders after
339 // this pass is finished.
340 changed = true;
341 }
342 }
343 }
344 }
345
346 if (changed) {
347 // We may have changed the block order and the dominator tree.
348 flow_graph->DiscoverBlocks();
349 GrowableArray<BitVector*> dominance_frontier;
350 flow_graph->ComputeDominators(&dominance_frontier);
351 }
352 }
353
354
355 } // namespace dart
OLDNEW
« no previous file with comments | « runtime/vm/branch_optimizer.h ('k') | runtime/vm/compiler.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698