OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, 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 interface OptimizationPhase { | |
6 String get name(); | |
7 void visitGraph(HGraph graph); | |
8 } | |
9 | |
10 class SsaOptimizerTask extends CompilerTask { | |
11 SsaOptimizerTask(Compiler compiler) : super(compiler); | |
12 String get name() => 'SSA optimizer'; | |
13 | |
14 void runPhases(HGraph graph, List<OptimizationPhase> phases) { | |
15 for (OptimizationPhase phase in phases) { | |
16 phase.visitGraph(graph); | |
17 compiler.tracer.traceGraph(phase.name, graph); | |
18 } | |
19 } | |
20 | |
21 void optimize(WorkItem work, HGraph graph) { | |
22 measure(() { | |
23 List<OptimizationPhase> phases = <OptimizationPhase>[ | |
24 new SsaConstantFolder(compiler), | |
25 new SsaRedundantPhiEliminator(), | |
26 new SsaDeadPhiEliminator(), | |
27 new SsaGlobalValueNumberer(compiler), | |
28 new SsaCodeMotion(), | |
29 new SsaDeadCodeEliminator()]; | |
30 runPhases(graph, phases); | |
31 }); | |
32 } | |
33 | |
34 bool trySpeculativeOptimizations(WorkItem work, HGraph graph) { | |
35 return measure(() { | |
36 // Run the phases that will generate type guards. We must also run | |
37 // [SsaCheckInserter] because the type propagator also propagates | |
38 // types non-speculatively. For example, it propagates the type | |
39 // array for a call to the List constructor. | |
40 List<OptimizationPhase> phases = <OptimizationPhase>[ | |
41 new SsaTypePropagator(compiler), | |
42 new SsaTypeGuardBuilder(compiler, work), | |
43 new SsaCheckInserter(compiler)]; | |
44 runPhases(graph, phases); | |
45 return !work.guards.isEmpty(); | |
46 }); | |
47 } | |
48 | |
49 void prepareForSpeculativeOptimizations(WorkItem work, HGraph graph) { | |
50 measure(() { | |
51 // In order to generate correct code for the bailout version, we did not | |
52 // propagate types from the instruction to the type guard. We do it | |
53 // now to be able to optimize further. | |
54 work.guards.forEach((HTypeGuard guard) { | |
55 guard.type = guard.guarded.type; | |
56 guard.guarded.type = HType.UNKNOWN; | |
57 }); | |
58 // We also need to insert range and integer checks for the type guards, | |
59 // now that they know their type. We did not need to do that | |
60 // before because instructions that reference a guard would | |
61 // have not tried to use, e.g. native array access, since the | |
62 // guard was not typed. | |
63 runPhases(graph, <OptimizationPhase>[new SsaCheckInserter(compiler)]); | |
64 }); | |
65 } | |
66 } | |
67 | |
68 /** | |
69 * If both inputs to known operations are available execute the operation at | |
70 * compile-time. | |
71 */ | |
72 class SsaConstantFolder extends HBaseVisitor implements OptimizationPhase { | |
73 final String name = "SsaConstantFolder"; | |
74 final Compiler compiler; | |
75 HGraph graph; | |
76 | |
77 SsaConstantFolder(this.compiler); | |
78 | |
79 void visitGraph(HGraph graph) { | |
80 this.graph = graph; | |
81 visitDominatorTree(graph); | |
82 } | |
83 | |
84 visitBasicBlock(HBasicBlock block) { | |
85 HInstruction instruction = block.first; | |
86 while (instruction !== null) { | |
87 HInstruction next = instruction.next; | |
88 HInstruction replacement = instruction.accept(this); | |
89 if (replacement !== instruction) { | |
90 if (!replacement.isInBasicBlock()) { | |
91 // The constant folding can return an instruction that is already | |
92 // part of the graph (like an input), so we only add the replacement | |
93 // if necessary. | |
94 block.addAfter(instruction, replacement); | |
95 } | |
96 block.rewrite(instruction, replacement); | |
97 block.remove(instruction); | |
98 // Because the constant folder runs after type propagation, we | |
99 // must update the type of this instruction manually. Later | |
100 // phases can then optimize this instruction based on its | |
101 // type. | |
102 replacement.updateType(); | |
103 } | |
104 instruction = next; | |
105 } | |
106 } | |
107 | |
108 HInstruction visitInstruction(HInstruction node) { | |
109 return node; | |
110 } | |
111 | |
112 HInstruction visitBoolify(HBoolify node) { | |
113 List<HInstruction> inputs = node.inputs; | |
114 assert(inputs.length == 1); | |
115 HInstruction input = inputs[0]; | |
116 if (input.isBoolean()) return input; | |
117 // All values !== true are boolified to false. | |
118 if (input.type.isKnown()) { | |
119 return graph.addConstantBool(false); | |
120 } | |
121 return node; | |
122 } | |
123 | |
124 HInstruction visitNot(HNot node) { | |
125 List<HInstruction> inputs = node.inputs; | |
126 assert(inputs.length == 1); | |
127 HInstruction input = inputs[0]; | |
128 if (input is HConstant) { | |
129 HConstant constant = input; | |
130 bool isTrue = constant.constant.isTrue(); | |
131 return graph.addConstantBool(!isTrue); | |
132 } | |
133 return node; | |
134 } | |
135 | |
136 HInstruction visitInvokeUnary(HInvokeUnary node) { | |
137 HInstruction operand = node.operand; | |
138 if (operand is HConstant) { | |
139 UnaryOperation operation = node.operation; | |
140 HConstant receiver = operand; | |
141 Constant folded = operation.fold(receiver.constant); | |
142 if (folded !== null) return graph.addConstant(folded); | |
143 } | |
144 return node; | |
145 } | |
146 | |
147 HInstruction visitInvokeInterceptor(HInvokeInterceptor node) { | |
148 if (node.name == const SourceString('length') && | |
149 node.inputs[1].isConstantString()) { | |
150 HConstant input = node.inputs[1]; | |
151 StringConstant constant = input.constant; | |
152 DartString string = constant.value; | |
153 return graph.addConstantInt(string.length); | |
154 } | |
155 return node; | |
156 } | |
157 | |
158 HInstruction visitInvokeBinary(HInvokeBinary node) { | |
159 HInstruction left = node.left; | |
160 HInstruction right = node.right; | |
161 if (left is HConstant && right is HConstant) { | |
162 BinaryOperation operation = node.operation; | |
163 HConstant op1 = left; | |
164 HConstant op2 = right; | |
165 Constant folded = operation.fold(op1.constant, op2.constant); | |
166 if (folded !== null) return graph.addConstant(folded); | |
167 } | |
168 return node; | |
169 } | |
170 | |
171 HInstruction visitEquals(HEquals node) { | |
172 HInstruction left = node.left; | |
173 HInstruction right = node.right; | |
174 if (!left.isConstant() && right.isConstantNull()) { | |
175 // TODO(floitsch): cache interceptors. | |
176 HStatic target = new HStatic( | |
177 compiler.builder.interceptors.getEqualsNullInterceptor()); | |
178 node.block.addBefore(node,target); | |
179 return new HEquals(target, node.left, node.right); | |
180 } | |
181 // All other cases are dealt with by the [visitInvokeBinary]. | |
182 return visitInvokeBinary(node); | |
183 } | |
184 | |
185 HInstruction visitTypeGuard(HTypeGuard node) { | |
186 HInstruction value = node.guarded; | |
187 return (value.type.combine(node.type) == value.type) ? value : node; | |
188 } | |
189 | |
190 HInstruction visitIntegerCheck(HIntegerCheck node) { | |
191 HInstruction value = node.value; | |
192 return value.isInteger() ? value : node; | |
193 } | |
194 | |
195 HInstruction visitIs(HIs node) { | |
196 Element element = node.typeExpression; | |
197 if (element.kind === ElementKind.TYPE_VARIABLE) { | |
198 compiler.unimplemented("visitIs for type variables"); | |
199 } | |
200 | |
201 HType expressionType = node.expression.type; | |
202 if (element === compiler.objectClass | |
203 || element === compiler.dynamicClass) { | |
204 return graph.addConstantBool(true); | |
205 } else if (expressionType.isInteger()) { | |
206 if (element === compiler.intClass || element === compiler.numClass) { | |
207 return graph.addConstantBool(true); | |
208 } else if (element === compiler.doubleClass) { | |
209 // We let the JS semantics decide for that check. Currently | |
210 // the code we emit will always return true. | |
211 return node; | |
212 } else { | |
213 return graph.addConstantBool(false); | |
214 } | |
215 } else if (expressionType.isDouble()) { | |
216 if (element === compiler.doubleClass || element === compiler.numClass) { | |
217 return graph.addConstantBool(true); | |
218 } else if (element === compiler.intClass) { | |
219 // We let the JS semantics decide for that check. Currently | |
220 // the code we emit will return true for a double that can be | |
221 // represented as a 31-bit integer. | |
222 return node; | |
223 } else { | |
224 return graph.addConstantBool(false); | |
225 } | |
226 } else if (expressionType.isNumber()) { | |
227 if (element === compiler.numClass) { | |
228 return graph.addConstantBool(true); | |
229 } | |
230 // We cannot just return false, because the expression may be of | |
231 // type int or double. | |
232 } else if (expressionType.isString()) { | |
233 if (element === compiler.stringClass | |
234 || Elements.isStringSupertype(element, compiler)) { | |
235 return graph.addConstantBool(true); | |
236 } else { | |
237 return graph.addConstantBool(false); | |
238 } | |
239 } else if (expressionType.isArray()) { | |
240 if (element === compiler.listClass | |
241 || Elements.isListSupertype(element, compiler)) { | |
242 return graph.addConstantBool(true); | |
243 } else { | |
244 return graph.addConstantBool(false); | |
245 } | |
246 } | |
247 return node; | |
248 } | |
249 } | |
250 | |
251 class SsaCheckInserter extends HBaseVisitor implements OptimizationPhase { | |
252 final String name = "SsaCheckInserter"; | |
253 Element lengthInterceptor; | |
254 | |
255 SsaCheckInserter(Compiler compiler) { | |
256 SourceString lengthString = const SourceString('length'); | |
257 lengthInterceptor = | |
258 compiler.builder.interceptors.getStaticGetInterceptor(lengthString); | |
259 } | |
260 | |
261 void visitGraph(HGraph graph) { | |
262 visitDominatorTree(graph); | |
263 } | |
264 | |
265 void visitBasicBlock(HBasicBlock block) { | |
266 HInstruction instruction = block.first; | |
267 while (instruction !== null) { | |
268 HInstruction next = instruction.next; | |
269 instruction = instruction.accept(this); | |
270 instruction = next; | |
271 } | |
272 } | |
273 | |
274 HBoundsCheck insertBoundsCheck(HInstruction node, | |
275 HInstruction receiver, | |
276 HInstruction index) { | |
277 HStatic interceptor = new HStatic(lengthInterceptor); | |
278 node.block.addBefore(node, interceptor); | |
279 HInvokeInterceptor length = new HInvokeInterceptor( | |
280 Selector.INVOCATION_0, | |
281 const SourceString("length"), | |
282 true, | |
283 <HInstruction>[interceptor, receiver]); | |
284 length.type = HType.NUMBER; | |
285 node.block.addBefore(node, length); | |
286 | |
287 HBoundsCheck check = new HBoundsCheck(length, index); | |
288 node.block.addBefore(node, check); | |
289 return check; | |
290 } | |
291 | |
292 HIntegerCheck insertIntegerCheck(HInstruction node, HInstruction value) { | |
293 HIntegerCheck check = new HIntegerCheck(value); | |
294 node.block.addBefore(node, check); | |
295 return check; | |
296 } | |
297 | |
298 void visitIndex(HIndex node) { | |
299 if (!node.builtin) return; | |
300 HInstruction index = insertIntegerCheck(node, node.index); | |
301 index = insertBoundsCheck(node, node.receiver, index); | |
302 HIndex newInstruction = new HIndex(node.target, node.receiver, index); | |
303 node.block.addBefore(node, newInstruction); | |
304 node.block.rewrite(node, newInstruction); | |
305 node.block.remove(node); | |
306 } | |
307 | |
308 void visitIndexAssign(HIndexAssign node) { | |
309 if (!node.builtin) return; | |
310 HInstruction index = insertIntegerCheck(node, node.index); | |
311 index = insertBoundsCheck(node, node.receiver, index); | |
312 HIndexAssign newInstruction = | |
313 new HIndexAssign(node.target, node.receiver, index, node.value); | |
314 node.block.addBefore(node, newInstruction); | |
315 node.block.rewrite(node, newInstruction); | |
316 node.block.remove(node); | |
317 } | |
318 } | |
319 | |
320 class SsaDeadCodeEliminator extends HGraphVisitor implements OptimizationPhase { | |
321 final String name = "SsaDeadCodeEliminator"; | |
322 | |
323 static bool isDeadCode(HInstruction instruction) { | |
324 // TODO(ngeoffray): the way we handle side effects is not right | |
325 // (e.g. branching instructions have side effects). | |
326 return !instruction.hasSideEffects() | |
327 && instruction.usedBy.isEmpty() | |
328 && instruction is !HCheck | |
329 && instruction is !HTypeGuard; | |
330 } | |
331 | |
332 void visitGraph(HGraph graph) { | |
333 visitPostDominatorTree(graph); | |
334 } | |
335 | |
336 void visitBasicBlock(HBasicBlock block) { | |
337 HInstruction instruction = block.last; | |
338 while (instruction !== null) { | |
339 var previous = instruction.previous; | |
340 if (isDeadCode(instruction)) block.remove(instruction); | |
341 instruction = previous; | |
342 } | |
343 } | |
344 } | |
345 | |
346 class SsaDeadPhiEliminator implements OptimizationPhase { | |
347 final String name = "SsaDeadPhiEliminator"; | |
348 | |
349 void visitGraph(HGraph graph) { | |
350 final List<HPhi> worklist = <HPhi>[]; | |
351 // A set to keep track of the live phis that we found. | |
352 final Set<HPhi> livePhis = new Set<HPhi>(); | |
353 | |
354 // Add to the worklist all live phis: phis referenced by non-phi | |
355 // instructions. | |
356 for (final block in graph.blocks) { | |
357 block.forEachPhi((HPhi phi) { | |
358 for (final user in phi.usedBy) { | |
359 if (user is !HPhi) { | |
360 worklist.add(phi); | |
361 livePhis.add(phi); | |
362 break; | |
363 } | |
364 } | |
365 }); | |
366 } | |
367 | |
368 // Process the worklist by propagating liveness to phi inputs. | |
369 while (!worklist.isEmpty()) { | |
370 HPhi phi = worklist.removeLast(); | |
371 for (final input in phi.inputs) { | |
372 if (input is HPhi && !livePhis.contains(input)) { | |
373 worklist.add(input); | |
374 livePhis.add(input); | |
375 } | |
376 } | |
377 } | |
378 | |
379 // Remove phis that are not live. | |
380 // Traverse in reverse order to remove phis with no uses before the | |
381 // phis that they might use. | |
382 // NOTICE: Doesn't handle circular references, but we don't currently | |
383 // create any. | |
384 List<HBasicBlock> blocks = graph.blocks; | |
385 for (int i = blocks.length - 1; i >= 0; i--) { | |
386 HBasicBlock block = blocks[i]; | |
387 HPhi current = block.phis.first; | |
388 HPhi next = null; | |
389 while (current != null) { | |
390 next = current.next; | |
391 if (!livePhis.contains(current) | |
392 // TODO(ahe): Not sure the following is correct. | |
393 && current.usedBy.isEmpty()) { | |
394 block.removePhi(current); | |
395 } | |
396 current = next; | |
397 } | |
398 } | |
399 } | |
400 } | |
401 | |
402 class SsaRedundantPhiEliminator implements OptimizationPhase { | |
403 final String name = "SsaRedundantPhiEliminator"; | |
404 | |
405 void visitGraph(HGraph graph) { | |
406 final List<HPhi> worklist = <HPhi>[]; | |
407 | |
408 // Add all phis in the worklist. | |
409 for (final block in graph.blocks) { | |
410 block.forEachPhi((HPhi phi) => worklist.add(phi)); | |
411 } | |
412 | |
413 while (!worklist.isEmpty()) { | |
414 HPhi phi = worklist.removeLast(); | |
415 | |
416 // If the phi has already been processed, continue. | |
417 if (!phi.isInBasicBlock()) continue; | |
418 | |
419 // Find if the inputs of the phi are the same instruction. | |
420 // The builder ensures that phi.inputs[0] cannot be the phi | |
421 // itself. | |
422 assert(phi.inputs[0] !== phi); | |
423 HInstruction candidate = phi.inputs[0]; | |
424 for (int i = 1; i < phi.inputs.length; i++) { | |
425 HInstruction input = phi.inputs[i]; | |
426 // If the input is the phi, the phi is still candidate for | |
427 // elimination. | |
428 if (input !== candidate && input !== phi) { | |
429 candidate = null; | |
430 break; | |
431 } | |
432 } | |
433 | |
434 // If the inputs are not the same, continue. | |
435 if (candidate == null) continue; | |
436 | |
437 // Because we're updating the users of this phi, we may have new | |
438 // phis candidate for elimination. Add phis that used this phi | |
439 // to the worklist. | |
440 for (final user in phi.usedBy) { | |
441 if (user is HPhi) worklist.add(user); | |
442 } | |
443 phi.block.rewrite(phi, candidate); | |
444 phi.block.removePhi(phi); | |
445 } | |
446 } | |
447 } | |
448 | |
449 class SsaGlobalValueNumberer implements OptimizationPhase { | |
450 final String name = "SsaGlobalValueNumberer"; | |
451 final Compiler compiler; | |
452 final Set<int> visited; | |
453 | |
454 List<int> blockChangesFlags; | |
455 List<int> loopChangesFlags; | |
456 | |
457 SsaGlobalValueNumberer(this.compiler) : visited = new Set<int>(); | |
458 | |
459 void visitGraph(HGraph graph) { | |
460 computeChangesFlags(graph); | |
461 moveLoopInvariantCode(graph); | |
462 visitBasicBlock(graph.entry, new ValueSet()); | |
463 } | |
464 | |
465 void moveLoopInvariantCode(HGraph graph) { | |
466 for (int i = graph.blocks.length - 1; i >= 0; i--) { | |
467 HBasicBlock block = graph.blocks[i]; | |
468 if (block.isLoopHeader()) { | |
469 int changesFlags = loopChangesFlags[block.id]; | |
470 HBasicBlock last = block.loopInformation.getLastBackEdge(); | |
471 for (int j = block.id; j <= last.id; j++) { | |
472 moveLoopInvariantCodeFromBlock(graph.blocks[j], block, changesFlags); | |
473 } | |
474 } | |
475 } | |
476 } | |
477 | |
478 void moveLoopInvariantCodeFromBlock(HBasicBlock block, | |
479 HBasicBlock loopHeader, | |
480 int changesFlags) { | |
481 HBasicBlock preheader = loopHeader.predecessors[0]; | |
482 int dependsFlags = HInstruction.computeDependsOnFlags(changesFlags); | |
483 HInstruction instruction = block.first; | |
484 while (instruction != null) { | |
485 HInstruction next = instruction.next; | |
486 if (instruction.useGvn() | |
487 && (instruction is !HCheck) | |
488 && (instruction.flags & dependsFlags) == 0) { | |
489 bool loopInvariantInputs = true; | |
490 List<HInstruction> inputs = instruction.inputs; | |
491 for (int i = 0, length = inputs.length; i < length; i++) { | |
492 if (isInputDefinedAfterDominator(inputs[i], preheader)) { | |
493 loopInvariantInputs = false; | |
494 break; | |
495 } | |
496 } | |
497 | |
498 // If the inputs are loop invariant, we can move the | |
499 // instruction from the current block to the pre-header block. | |
500 if (loopInvariantInputs) { | |
501 block.detach(instruction); | |
502 preheader.moveAtExit(instruction); | |
503 } | |
504 } | |
505 instruction = next; | |
506 } | |
507 } | |
508 | |
509 bool isInputDefinedAfterDominator(HInstruction input, | |
510 HBasicBlock dominator) { | |
511 return input.block.id > dominator.id; | |
512 } | |
513 | |
514 void visitBasicBlock(HBasicBlock block, ValueSet values) { | |
515 HInstruction instruction = block.first; | |
516 while (instruction !== null) { | |
517 HInstruction next = instruction.next; | |
518 int flags = instruction.getChangesFlags(); | |
519 if (flags != 0) { | |
520 assert(!instruction.useGvn()); | |
521 values.kill(flags); | |
522 } else if (instruction.useGvn()) { | |
523 HInstruction other = values.lookup(instruction); | |
524 if (other !== null) { | |
525 assert(other.gvnEquals(instruction) && instruction.gvnEquals(other)); | |
526 block.rewrite(instruction, other); | |
527 block.remove(instruction); | |
528 } else { | |
529 values.add(instruction); | |
530 } | |
531 } | |
532 instruction = next; | |
533 } | |
534 | |
535 List<HBasicBlock> dominatedBlocks = block.dominatedBlocks; | |
536 for (int i = 0, length = dominatedBlocks.length; i < length; i++) { | |
537 HBasicBlock dominated = dominatedBlocks[i]; | |
538 // No need to copy the value set for the last child. | |
539 ValueSet successorValues = (i == length - 1) ? values : values.copy(); | |
540 // If we have no values in our set, we do not have to kill | |
541 // anything. Also, if the range of block ids from the current | |
542 // block to the dominated block is empty, there is no blocks on | |
543 // any path from the current block to the dominated block so we | |
544 // don't have to do anything either. | |
545 assert(block.id < dominated.id); | |
546 if (!successorValues.isEmpty() && block.id + 1 < dominated.id) { | |
547 visited.clear(); | |
548 int changesFlags = getChangesFlagsForDominatedBlock(block, dominated); | |
549 successorValues.kill(changesFlags); | |
550 } | |
551 visitBasicBlock(dominated, successorValues); | |
552 } | |
553 } | |
554 | |
555 void computeChangesFlags(HGraph graph) { | |
556 // Create the changes flags lists. Make sure to initialize the | |
557 // loop changes flags list to zero so we can use bitwise or when | |
558 // propagating loop changes upwards. | |
559 final int length = graph.blocks.length; | |
560 blockChangesFlags = new List<int>(length); | |
561 loopChangesFlags = new List<int>(length); | |
562 for (int i = 0; i < length; i++) loopChangesFlags[i] = 0; | |
563 | |
564 // Run through all the basic blocks in the graph and fill in the | |
565 // changes flags lists. | |
566 for (int i = length - 1; i >= 0; i--) { | |
567 final HBasicBlock block = graph.blocks[i]; | |
568 final int id = block.id; | |
569 | |
570 // Compute block changes flags for the block. | |
571 int changesFlags = 0; | |
572 HInstruction instruction = block.first; | |
573 while (instruction !== null) { | |
574 instruction.prepareGvn(); | |
575 changesFlags |= instruction.getChangesFlags(); | |
576 instruction = instruction.next; | |
577 } | |
578 assert(blockChangesFlags[id] === null); | |
579 blockChangesFlags[id] = changesFlags; | |
580 | |
581 // Loop headers are part of their loop, so update the loop | |
582 // changes flags accordingly. | |
583 if (block.isLoopHeader()) { | |
584 loopChangesFlags[id] |= changesFlags; | |
585 } | |
586 | |
587 // Propagate loop changes flags upwards. | |
588 HBasicBlock parentLoopHeader = block.parentLoopHeader; | |
589 if (parentLoopHeader !== null) { | |
590 loopChangesFlags[parentLoopHeader.id] |= (block.isLoopHeader()) | |
591 ? loopChangesFlags[id] | |
592 : changesFlags; | |
593 } | |
594 } | |
595 } | |
596 | |
597 int getChangesFlagsForDominatedBlock(HBasicBlock dominator, | |
598 HBasicBlock dominated) { | |
599 int changesFlags = 0; | |
600 List<HBasicBlock> predecessors = dominated.predecessors; | |
601 for (int i = 0, length = predecessors.length; i < length; i++) { | |
602 HBasicBlock block = predecessors[i]; | |
603 int id = block.id; | |
604 // If the current predecessor block is on the path from the | |
605 // dominator to the dominated, it must have an id that is in the | |
606 // range from the dominator to the dominated. | |
607 if (dominator.id < id && id < dominated.id && !visited.contains(id)) { | |
608 visited.add(id); | |
609 changesFlags |= blockChangesFlags[id]; | |
610 changesFlags |= getChangesFlagsForDominatedBlock(dominator, block); | |
611 } | |
612 } | |
613 return changesFlags; | |
614 } | |
615 } | |
616 | |
617 // This phase merges equivalent instructions on different paths into | |
618 // one instruction in a dominator block. It runs through the graph | |
619 // post dominator order and computes a ValueSet for each block of | |
620 // instructions that can be moved to a dominator block. These | |
621 // instructions are the ones that: | |
622 // 1) can be used for GVN, and | |
623 // 2) do not use definitions of their own block. | |
624 // | |
625 // A basic block looks at its sucessors and finds the intersection of | |
626 // these computed ValueSet. It moves all instructions of the | |
627 // intersection into its own list of instructions. | |
628 class SsaCodeMotion extends HBaseVisitor implements OptimizationPhase { | |
629 final String name = "SsaCodeMotion"; | |
630 | |
631 List<ValueSet> values; | |
632 | |
633 void visitGraph(HGraph graph) { | |
634 values = new List<ValueSet>(graph.blocks.length); | |
635 for (int i = 0; i < graph.blocks.length; i++) { | |
636 values[graph.blocks[i].id] = new ValueSet(); | |
637 } | |
638 visitPostDominatorTree(graph); | |
639 } | |
640 | |
641 void visitBasicBlock(HBasicBlock block) { | |
642 List<HBasicBlock> successors = block.successors; | |
643 | |
644 // Phase 1: get the ValueSet of all successors, compute the | |
645 // intersection and move the instructions of the intersection into | |
646 // this block. | |
647 if (successors.length != 0) { | |
648 ValueSet instructions = values[successors[0].id]; | |
649 for (int i = 1; i < successors.length; i++) { | |
650 ValueSet other = values[successors[i].id]; | |
651 instructions = instructions.intersection(other); | |
652 } | |
653 | |
654 if (!instructions.isEmpty()) { | |
655 List<HInstruction> list = instructions.toList(); | |
656 for (HInstruction instruction in list) { | |
657 // Move the instruction to the current block. | |
658 instruction.block.detach(instruction); | |
659 block.moveAtExit(instruction); | |
660 // Go through all successors and rewrite their instruction | |
661 // to the shared one. | |
662 for (final successor in successors) { | |
663 HInstruction toRewrite = values[successor.id].lookup(instruction); | |
664 if (toRewrite != instruction) { | |
665 successor.rewrite(toRewrite, instruction); | |
666 successor.remove(toRewrite); | |
667 } | |
668 } | |
669 } | |
670 } | |
671 } | |
672 | |
673 // Don't try to merge instructions to a dominator if we have | |
674 // multiple predecessors. | |
675 if (block.predecessors.length != 1) return; | |
676 | |
677 // Phase 2: Go through all instructions of this block and find | |
678 // which instructions can be moved to a dominator block. | |
679 ValueSet set_ = values[block.id]; | |
680 HInstruction instruction = block.first; | |
681 int flags = 0; | |
682 while (instruction !== null) { | |
683 int dependsFlags = HInstruction.computeDependsOnFlags(flags); | |
684 flags |= instruction.getChangesFlags(); | |
685 | |
686 HInstruction current = instruction; | |
687 instruction = instruction.next; | |
688 | |
689 // TODO(ngeoffray): this check is needed because we currently do | |
690 // not have flags to express 'Gvn'able', but not movable. | |
691 if (current is HCheck) continue; | |
692 if (!current.useGvn()) continue; | |
693 if ((current.flags & dependsFlags) != 0) continue; | |
694 | |
695 bool canBeMoved = true; | |
696 for (final HInstruction input in current.inputs) { | |
697 if (input.block == block) { | |
698 canBeMoved = false; | |
699 break; | |
700 } | |
701 } | |
702 if (!canBeMoved) continue; | |
703 | |
704 // This is safe because we are running after GVN. | |
705 // TODO(ngeoffray): ensure GVN has been run. | |
706 set_.add(current); | |
707 } | |
708 } | |
709 } | |
OLD | NEW |