OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012, 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 part of ssa; | |
6 | |
7 /** | |
8 * Replaces some instructions with specialized versions to make codegen easier. | |
9 * Caches codegen information on nodes. | |
10 */ | |
11 class SsaInstructionSelection extends HBaseVisitor { | |
12 final Compiler compiler; | |
13 HGraph graph; | |
14 | |
15 SsaInstructionSelection(this.compiler); | |
16 | |
17 JavaScriptBackend get backend => compiler.backend; | |
18 | |
19 void visitGraph(HGraph graph) { | |
20 this.graph = graph; | |
21 visitDominatorTree(graph); | |
22 } | |
23 | |
24 visitBasicBlock(HBasicBlock block) { | |
25 HInstruction instruction = block.first; | |
26 while (instruction != null) { | |
27 HInstruction next = instruction.next; | |
28 HInstruction replacement = instruction.accept(this); | |
29 if (replacement != instruction && replacement != null) { | |
30 block.rewrite(instruction, replacement); | |
31 | |
32 // If the replacement instruction does not know its source element, use | |
33 // the source element of the instruction. | |
34 if (replacement.sourceElement == null) { | |
35 replacement.sourceElement = instruction.sourceElement; | |
36 } | |
37 if (replacement.sourcePosition == null) { | |
38 replacement.sourcePosition = instruction.sourcePosition; | |
39 } | |
40 if (!replacement.isInBasicBlock()) { | |
41 // The constant folding can return an instruction that is already | |
42 // part of the graph (like an input), so we only add the replacement | |
43 // if necessary. | |
44 block.addAfter(instruction, replacement); | |
45 // Visit the replacement as the next instruction in case it can also | |
46 // be constant folded away. | |
47 next = replacement; | |
48 } | |
49 block.remove(instruction); | |
50 } | |
51 instruction = next; | |
52 } | |
53 } | |
54 | |
55 HInstruction visitInstruction(HInstruction node) { | |
56 return node; | |
57 } | |
58 | |
59 HInstruction visitIs(HIs node) { | |
60 if (node.kind == HIs.RAW_CHECK) { | |
61 HInstruction interceptor = node.interceptor; | |
62 if (interceptor != null) { | |
63 return new HIsViaInterceptor(node.typeExpression, interceptor, | |
64 backend.boolType); | |
65 } | |
66 } | |
67 return node; | |
68 } | |
69 | |
70 HInstruction visitIdentity(HIdentity node) { | |
71 node.singleComparisonOp = simpleOp(node.left, node.right); | |
72 return node; | |
73 } | |
74 | |
75 String simpleOp(HInstruction left, HInstruction right) { | |
76 // Returns the single identity comparison (== or ===) or null if a more | |
77 // complex expression is required. | |
78 TypeMask leftType = left.instructionType; | |
79 TypeMask rightType = right.instructionType; | |
80 if (leftType.isNullable && rightType.isNullable) { | |
81 if (left.isConstantNull() || | |
82 right.isConstantNull() || | |
83 (left.isPrimitive(compiler) && | |
84 leftType == rightType)) { | |
85 return '=='; | |
86 } | |
87 return null; | |
88 } | |
89 return '==='; | |
90 } | |
91 | |
92 HInstruction visitInvokeDynamic(HInvokeDynamic node) { | |
93 if (node.isInterceptedCall) { | |
94 // Calls of the form | |
95 // | |
96 // a.foo$1(a, x) | |
97 // | |
98 // where the interceptor calling convention is used come from recognizing | |
99 // that 'a' is a 'self-interceptor'. If the selector matches only methods | |
100 // that ignore the explicit receiver parameter, replace occurences of the | |
101 // receiver argument with a dummy receiver '0': | |
102 // | |
103 // a.foo$1(a, x) ---> a.foo$1(0, x) | |
104 // | |
105 // This often reduces the number of references to 'a' to one, allowing 'a' | |
106 // to be generated at use to avoid a temporary, e.g. | |
107 // | |
108 // t1 = b.get$thing(); | |
109 // t1.foo$1(t1, x) | |
110 // ---> | |
111 // b.get$thing().foo$1(0, x) | |
112 // | |
113 Selector selector = node.selector; | |
114 if (backend.isInterceptedSelector(selector) && | |
115 !backend.isInterceptedMixinSelector(selector)) { | |
116 HInstruction interceptor = node.inputs[0]; | |
117 HInstruction receiverArgument = node.inputs[1]; | |
118 | |
119 if (interceptor.nonCheck() == receiverArgument.nonCheck()) { | |
120 // TODO(15933): Make automatically generated property extraction | |
121 // closures work with the dummy receiver optimization. | |
122 if (!selector.isGetter) { | |
123 ConstantValue constant = new DummyConstantValue( | |
124 receiverArgument.instructionType); | |
125 HConstant dummy = graph.addConstant(constant, compiler); | |
126 receiverArgument.usedBy.remove(node); | |
127 node.inputs[1] = dummy; | |
128 dummy.usedBy.add(node); | |
129 } | |
130 } | |
131 } | |
132 } | |
133 | |
134 return node; | |
135 } | |
136 | |
137 HInstruction visitFieldSet(HFieldSet setter) { | |
138 // Pattern match | |
139 // t1 = x.f; t2 = t1 + 1; x.f = t2; use(t2) --> ++x.f | |
140 // t1 = x.f; t2 = t1 op y; x.f = t2; use(t2) --> x.f op= y | |
141 // t1 = x.f; t2 = t1 + 1; x.f = t2; use(t1) --> x.f++ | |
142 HBasicBlock block = setter.block; | |
143 HInstruction op = setter.value; | |
144 HInstruction receiver = setter.receiver; | |
145 | |
146 bool isMatchingRead(HInstruction candidate) { | |
147 if (candidate is HFieldGet) { | |
148 if (candidate.element != setter.element) return false; | |
149 if (candidate.receiver != setter.receiver) return false; | |
150 // Recognize only three instructions in sequence in the same block. Thi
s | |
151 // could be broadened to allow non-interfering interleaved instructions. | |
152 if (op.block != block) return false; | |
153 if (candidate.block != block) return false; | |
154 if (setter.previous != op) return false; | |
155 if (op.previous != candidate) return false; | |
156 return true; | |
157 } | |
158 return false; | |
159 } | |
160 | |
161 HInstruction noMatchingRead() { | |
162 // If we have other HFieldSet optimizations, they go here. | |
163 return null; | |
164 } | |
165 | |
166 HInstruction replaceOp(HInstruction replacement, HInstruction getter) { | |
167 block.addBefore(setter, replacement); | |
168 block.remove(setter); | |
169 block.rewrite(op, replacement); | |
170 block.remove(op); | |
171 block.remove(getter); | |
172 return null; | |
173 } | |
174 | |
175 HInstruction plusOrMinus(String assignOp, String incrementOp) { | |
176 HInvokeBinary binary = op; | |
177 HInstruction left = binary.left; | |
178 HInstruction right = binary.right; | |
179 if (isMatchingRead(left)) { | |
180 if (left.usedBy.length == 1) { | |
181 if (right is HConstant && right.constant.isOne) { | |
182 HInstruction rmw = new HReadModifyWrite.preOp( | |
183 setter.element, incrementOp, receiver, op.instructionType); | |
184 return replaceOp(rmw, left); | |
185 } else { | |
186 HInstruction rmw = new HReadModifyWrite.assignOp( | |
187 setter.element, | |
188 assignOp, | |
189 receiver, right, op.instructionType); | |
190 return replaceOp(rmw, left); | |
191 } | |
192 } else if (op.usedBy.length == 1 && | |
193 right is HConstant && | |
194 right.constant.isOne) { | |
195 HInstruction rmw = new HReadModifyWrite.postOp( | |
196 setter.element, incrementOp, receiver, op.instructionType); | |
197 block.addAfter(left, rmw); | |
198 block.remove(setter); | |
199 block.remove(op); | |
200 block.rewrite(left, rmw); | |
201 block.remove(left); | |
202 return null; | |
203 } | |
204 } | |
205 return noMatchingRead(); | |
206 } | |
207 | |
208 HInstruction simple(String assignOp, | |
209 HInstruction left, HInstruction right) { | |
210 if (isMatchingRead(left)) { | |
211 if (left.usedBy.length == 1) { | |
212 HInstruction rmw = new HReadModifyWrite.assignOp( | |
213 setter.element, | |
214 assignOp, | |
215 receiver, right, op.instructionType); | |
216 return replaceOp(rmw, left); | |
217 } | |
218 } | |
219 return noMatchingRead(); | |
220 } | |
221 | |
222 HInstruction simpleBinary(String assignOp) { | |
223 HInvokeBinary binary = op; | |
224 return simple(assignOp, binary.left, binary.right); | |
225 } | |
226 | |
227 HInstruction bitop(String assignOp) { | |
228 // HBitAnd, HBitOr etc. are more difficult because HBitAnd(a.x, y) | |
229 // sometimes needs to be forced to unsigned: a.x = (a.x & y) >>> 0. | |
230 if (op.isUInt31(compiler)) return simpleBinary(assignOp); | |
231 return noMatchingRead(); | |
232 } | |
233 | |
234 if (op is HAdd) return plusOrMinus('+', '++'); | |
235 if (op is HSubtract) return plusOrMinus('-', '--'); | |
236 | |
237 if (op is HStringConcat) return simple('+', op.left, op.right); | |
238 | |
239 if (op is HMultiply) return simpleBinary('*'); | |
240 if (op is HDivide) return simpleBinary('/'); | |
241 | |
242 if (op is HBitAnd) return bitop('&'); | |
243 if (op is HBitOr) return bitop('|'); | |
244 if (op is HBitXor) return bitop('^'); | |
245 | |
246 return noMatchingRead(); | |
247 } | |
248 } | |
249 | |
250 /** | |
251 * Remove [HTypeKnown] instructions from the graph, to make codegen | |
252 * analysis easier. | |
253 */ | |
254 class SsaTypeKnownRemover extends HBaseVisitor { | |
255 | |
256 void visitGraph(HGraph graph) { | |
257 visitDominatorTree(graph); | |
258 } | |
259 | |
260 void visitBasicBlock(HBasicBlock block) { | |
261 HInstruction instruction = block.first; | |
262 while (instruction != null) { | |
263 HInstruction next = instruction.next; | |
264 instruction.accept(this); | |
265 instruction = next; | |
266 } | |
267 } | |
268 | |
269 void visitTypeKnown(HTypeKnown instruction) { | |
270 instruction.block.rewrite(instruction, instruction.checkedInput); | |
271 instruction.block.remove(instruction); | |
272 } | |
273 } | |
274 | |
275 /** | |
276 * Instead of emitting each SSA instruction with a temporary variable | |
277 * mark instructions that can be emitted at their use-site. | |
278 * For example, in: | |
279 * t0 = 4; | |
280 * t1 = 3; | |
281 * t2 = add(t0, t1); | |
282 * t0 and t1 would be marked and the resulting code would then be: | |
283 * t2 = add(4, 3); | |
284 */ | |
285 class SsaInstructionMerger extends HBaseVisitor { | |
286 final Compiler compiler; | |
287 /** | |
288 * List of [HInstruction] that the instruction merger expects in | |
289 * order when visiting the inputs of an instruction. | |
290 */ | |
291 List<HInstruction> expectedInputs; | |
292 /** | |
293 * Set of pure [HInstruction] that the instruction merger expects to | |
294 * find. The order of pure instructions do not matter, as they will | |
295 * not be affected by side effects. | |
296 */ | |
297 Set<HInstruction> pureInputs; | |
298 Set<HInstruction> generateAtUseSite; | |
299 | |
300 void markAsGenerateAtUseSite(HInstruction instruction) { | |
301 assert(!instruction.isJsStatement()); | |
302 generateAtUseSite.add(instruction); | |
303 } | |
304 | |
305 SsaInstructionMerger(this.generateAtUseSite, this.compiler); | |
306 | |
307 void visitGraph(HGraph graph) { | |
308 visitDominatorTree(graph); | |
309 } | |
310 | |
311 void analyzeInputs(HInstruction user, int start) { | |
312 List<HInstruction> inputs = user.inputs; | |
313 for (int i = start; i < inputs.length; i++) { | |
314 HInstruction input = inputs[i]; | |
315 if (!generateAtUseSite.contains(input) | |
316 && !input.isCodeMotionInvariant() | |
317 && input.usedBy.length == 1 | |
318 && input is !HPhi | |
319 && input is !HLocalValue | |
320 && !input.isJsStatement()) { | |
321 if (input.isPure()) { | |
322 // Only consider a pure input if it is in the same loop. | |
323 // Otherwise, we might move GVN'ed instruction back into the | |
324 // loop. | |
325 if (user.hasSameLoopHeaderAs(input)) { | |
326 // Move it closer to [user], so that instructions in | |
327 // between do not prevent making it generate at use site. | |
328 input.moveBefore(user); | |
329 pureInputs.add(input); | |
330 // Previous computations done on [input] are now invalid | |
331 // because we moved [input] to another place. So all | |
332 // non code motion invariant instructions need | |
333 // to be removed from the [generateAtUseSite] set. | |
334 input.inputs.forEach((instruction) { | |
335 if (!instruction.isCodeMotionInvariant()) { | |
336 generateAtUseSite.remove(instruction); | |
337 } | |
338 }); | |
339 // Visit the pure input now so that the expected inputs | |
340 // are after the expected inputs of [user]. | |
341 input.accept(this); | |
342 } | |
343 } else { | |
344 expectedInputs.add(input); | |
345 } | |
346 } | |
347 } | |
348 } | |
349 | |
350 void visitInstruction(HInstruction instruction) { | |
351 // A code motion invariant instruction is dealt before visiting it. | |
352 assert(!instruction.isCodeMotionInvariant()); | |
353 analyzeInputs(instruction, 0); | |
354 } | |
355 | |
356 // The codegen might use the input multiple times, so it must not be | |
357 // set generate at use site. | |
358 void visitIs(HIs instruction) {} | |
359 | |
360 // A bounds check method must not have its first input generated at use site, | |
361 // because it's using it twice. | |
362 void visitBoundsCheck(HBoundsCheck instruction) { | |
363 analyzeInputs(instruction, 1); | |
364 } | |
365 | |
366 // An identity operation must only have its inputs generated at use site if | |
367 // does not require an expression with multiple uses (because of null / | |
368 // undefined). | |
369 void visitIdentity(HIdentity instruction) { | |
370 if (instruction.singleComparisonOp != null) { | |
371 super.visitIdentity(instruction); | |
372 } | |
373 // Do nothing. | |
374 } | |
375 | |
376 void visitTypeConversion(HTypeConversion instruction) { | |
377 if (!instruction.isArgumentTypeCheck | |
378 && !instruction.isReceiverTypeCheck) { | |
379 assert(instruction.isCheckedModeCheck || instruction.isCastTypeCheck); | |
380 // Checked mode checks and cast checks compile to code that | |
381 // only use their input once, so we can safely visit them | |
382 // and try to merge the input. | |
383 visitInstruction(instruction); | |
384 } | |
385 } | |
386 | |
387 void visitTypeKnown(HTypeKnown instruction) { | |
388 // [HTypeKnown] instructions are removed before code generation. | |
389 assert(false); | |
390 } | |
391 | |
392 void tryGenerateAtUseSite(HInstruction instruction) { | |
393 if (instruction.isControlFlow()) return; | |
394 markAsGenerateAtUseSite(instruction); | |
395 } | |
396 | |
397 bool isBlockSinglePredecessor(HBasicBlock block) { | |
398 return block.successors.length == 1 | |
399 && block.successors[0].predecessors.length == 1; | |
400 } | |
401 | |
402 void visitBasicBlock(HBasicBlock block) { | |
403 // Compensate from not merging blocks: if the block is the | |
404 // single predecessor of its single successor, let the successor | |
405 // visit it. | |
406 if (isBlockSinglePredecessor(block)) return; | |
407 | |
408 tryMergingExpressions(block); | |
409 } | |
410 | |
411 void tryMergingExpressions(HBasicBlock block) { | |
412 // Visit each instruction of the basic block in last-to-first order. | |
413 // Keep a list of expected inputs of the current "expression" being | |
414 // merged. If instructions occur in the expected order, they are | |
415 // included in the expression. | |
416 | |
417 // The expectedInputs list holds non-trivial instructions that may | |
418 // be generated at their use site, if they occur in the correct order. | |
419 if (expectedInputs == null) expectedInputs = new List<HInstruction>(); | |
420 if (pureInputs == null) pureInputs = new Set<HInstruction>(); | |
421 | |
422 // Pop instructions from expectedInputs until instruction is found. | |
423 // Return true if it is found, or false if not. | |
424 bool findInInputsAndPopNonMatching(HInstruction instruction) { | |
425 assert(!instruction.isPure()); | |
426 while (!expectedInputs.isEmpty) { | |
427 HInstruction nextInput = expectedInputs.removeLast(); | |
428 assert(!generateAtUseSite.contains(nextInput)); | |
429 assert(nextInput.usedBy.length == 1); | |
430 if (identical(nextInput, instruction)) { | |
431 return true; | |
432 } | |
433 } | |
434 return false; | |
435 } | |
436 | |
437 block.last.accept(this); | |
438 for (HInstruction instruction = block.last.previous; | |
439 instruction != null; | |
440 instruction = instruction.previous) { | |
441 if (generateAtUseSite.contains(instruction)) { | |
442 continue; | |
443 } | |
444 if (instruction.isCodeMotionInvariant()) { | |
445 markAsGenerateAtUseSite(instruction); | |
446 continue; | |
447 } | |
448 if (instruction.isPure()) { | |
449 if (pureInputs.contains(instruction)) { | |
450 tryGenerateAtUseSite(instruction); | |
451 } else { | |
452 // If the input is not in the [pureInputs] set, it has not | |
453 // been visited or should not be generated at use-site. The most | |
454 // likely reason for the latter, is that the instruction is used | |
455 // in more than one location. | |
456 // We must either clear the expectedInputs, or move the pure | |
457 // instruction's inputs in front of the existing ones. | |
458 // Example: | |
459 // t1 = foo(); // side-effect. | |
460 // t2 = bar(); // side-effect. | |
461 // t3 = pure(t2); // used more than once. | |
462 // f(t1, t3); // expected inputs of 'f': t1. | |
463 // use(t3); | |
464 // | |
465 // If we don't clear the expected inputs we end up in a situation | |
466 // where pure pushes "t2" on top of "t1" leading to: | |
467 // t3 = pure(bar()); | |
468 // f(foo(), t3); | |
469 // use(t3); | |
470 // | |
471 // If we clear the expected-inputs list we have the correct | |
472 // output: | |
473 // t1 = foo(); | |
474 // t3 = pure(bar()); | |
475 // f(t1, t3); | |
476 // use(t3); | |
477 // | |
478 // Clearing is, however, not optimal. | |
479 // Example: | |
480 // t1 = foo(); // t1 is now used by `pure`. | |
481 // t2 = bar(); // t2 is now used by `f`. | |
482 // t3 = pure(t1); | |
483 // f(t2, t3); | |
484 // use(t3); | |
485 // | |
486 // If we clear the expected-inputs we can't generate-at-use any of | |
487 // the instructions. | |
488 // | |
489 // The optimal solution is to move the inputs of 'pure' in | |
490 // front of the expectedInputs list. This makes sense, since we | |
491 // push expected-inputs from left-to right, and the `pure` function | |
492 // invocation is "more left" (i.e. before) the first argument of `f`. | |
493 // With that approach we end up with: | |
494 // t3 = pure(foo(); | |
495 // f(bar(), t3); | |
496 // use(t3); | |
497 // | |
498 int oldLength = expectedInputs.length; | |
499 instruction.accept(this); | |
500 if (oldLength != 0 && oldLength != expectedInputs.length) { | |
501 // Move the pure instruction's inputs to the front. | |
502 List<HInstruction> newInputs = expectedInputs.sublist(oldLength); | |
503 int newCount = newInputs.length; | |
504 expectedInputs.setRange( | |
505 newCount, newCount + oldLength, expectedInputs); | |
506 expectedInputs.setRange(0, newCount, newInputs); | |
507 } | |
508 } | |
509 } else { | |
510 if (findInInputsAndPopNonMatching(instruction)) { | |
511 // The current instruction is the next non-trivial | |
512 // expected input. | |
513 tryGenerateAtUseSite(instruction); | |
514 } else { | |
515 assert(expectedInputs.isEmpty); | |
516 } | |
517 instruction.accept(this); | |
518 } | |
519 } | |
520 | |
521 if (block.predecessors.length == 1 | |
522 && isBlockSinglePredecessor(block.predecessors[0])) { | |
523 assert(block.phis.isEmpty); | |
524 tryMergingExpressions(block.predecessors[0]); | |
525 } else { | |
526 expectedInputs = null; | |
527 pureInputs = null; | |
528 } | |
529 } | |
530 } | |
531 | |
532 /** | |
533 * Detect control flow arising from short-circuit logical and | |
534 * conditional operators, and prepare the program to be generated | |
535 * using these operators instead of nested ifs and boolean variables. | |
536 */ | |
537 class SsaConditionMerger extends HGraphVisitor { | |
538 Set<HInstruction> generateAtUseSite; | |
539 Set<HInstruction> controlFlowOperators; | |
540 | |
541 void markAsGenerateAtUseSite(HInstruction instruction) { | |
542 assert(!instruction.isJsStatement()); | |
543 generateAtUseSite.add(instruction); | |
544 } | |
545 | |
546 SsaConditionMerger(this.generateAtUseSite, this.controlFlowOperators); | |
547 | |
548 void visitGraph(HGraph graph) { | |
549 visitPostDominatorTree(graph); | |
550 } | |
551 | |
552 /** | |
553 * Check if a block has at least one statement other than | |
554 * [instruction]. | |
555 */ | |
556 bool hasAnyStatement(HBasicBlock block, HInstruction instruction) { | |
557 // If [instruction] is not in [block], then if the block is not | |
558 // empty, we know there will be a statement to emit. | |
559 if (!identical(instruction.block, block)) return !identical(block.last, bloc
k.first); | |
560 | |
561 // If [instruction] is not the last instruction of the block | |
562 // before the control flow instruction, or the last instruction, | |
563 // then we will have to emit a statement for that last instruction. | |
564 if (instruction != block.last | |
565 && !identical(instruction, block.last.previous)) return true; | |
566 | |
567 // If one of the instructions in the block until [instruction] is | |
568 // not generated at use site, then we will have to emit a | |
569 // statement for it. | |
570 // TODO(ngeoffray): we could generate a comma separated | |
571 // list of expressions. | |
572 for (HInstruction temp = block.first; | |
573 !identical(temp, instruction); | |
574 temp = temp.next) { | |
575 if (!generateAtUseSite.contains(temp)) return true; | |
576 } | |
577 | |
578 return false; | |
579 } | |
580 | |
581 bool isSafeToGenerateAtUseSite(HInstruction user, HInstruction input) { | |
582 // A [HForeign] instruction uses operators and if we generate | |
583 // [input] at use site, the precedence might be wrong. | |
584 if (user is HForeign) return false; | |
585 // A [HCheck] instruction with control flow uses its input | |
586 // multiple times, so we avoid generating it at use site. | |
587 if (user is HCheck && user.isControlFlow()) return false; | |
588 // A [HIs] instruction uses its input multiple times, so we | |
589 // avoid generating it at use site. | |
590 if (user is HIs) return false; | |
591 return true; | |
592 } | |
593 | |
594 void visitBasicBlock(HBasicBlock block) { | |
595 if (block.last is !HIf) return; | |
596 HIf startIf = block.last; | |
597 HBasicBlock end = startIf.joinBlock; | |
598 | |
599 // We check that the structure is the following: | |
600 // If | |
601 // / \ | |
602 // / \ | |
603 // 1 expr goto | |
604 // goto / | |
605 // \ / | |
606 // \ / | |
607 // phi(expr, true|false) | |
608 // | |
609 // and the same for nested nodes: | |
610 // | |
611 // If | |
612 // / \ | |
613 // / \ | |
614 // 1 expr1 \ | |
615 // If \ | |
616 // / \ \ | |
617 // / \ goto | |
618 // 1 expr2 | | |
619 // goto goto | | |
620 // \ / | | |
621 // \ / | | |
622 // phi1(expr2, true|false) | |
623 // \ | | |
624 // \ | | |
625 // phi(phi1, true|false) | |
626 | |
627 if (end == null) return; | |
628 if (end.phis.isEmpty) return; | |
629 if (!identical(end.phis.first, end.phis.last)) return; | |
630 HBasicBlock elseBlock = startIf.elseBlock; | |
631 | |
632 if (!identical(end.predecessors[1], elseBlock)) return; | |
633 HPhi phi = end.phis.first; | |
634 HInstruction thenInput = phi.inputs[0]; | |
635 HInstruction elseInput = phi.inputs[1]; | |
636 if (thenInput.isJsStatement() || elseInput.isJsStatement()) return; | |
637 | |
638 if (hasAnyStatement(elseBlock, elseInput)) return; | |
639 assert(elseBlock.successors.length == 1); | |
640 assert(end.predecessors.length == 2); | |
641 | |
642 HBasicBlock thenBlock = startIf.thenBlock; | |
643 // Skip trivial goto blocks. | |
644 while (thenBlock.successors[0] != end && thenBlock.first is HGoto) { | |
645 thenBlock = thenBlock.successors[0]; | |
646 } | |
647 | |
648 // If the [thenBlock] is already a control flow operation, and does not | |
649 // have any statement and its join block is [end], we can emit a | |
650 // sequence of control flow operation. | |
651 if (controlFlowOperators.contains(thenBlock.last)) { | |
652 HIf otherIf = thenBlock.last; | |
653 if (!identical(otherIf.joinBlock, end)) { | |
654 // This could be a join block that just feeds into our join block. | |
655 HBasicBlock otherJoin = otherIf.joinBlock; | |
656 if (otherJoin.first != otherJoin.last) return; | |
657 if (otherJoin.successors.length != 1) return; | |
658 if (otherJoin.successors[0] != end) return; | |
659 if (otherJoin.phis.isEmpty) return; | |
660 if (!identical(otherJoin.phis.first, otherJoin.phis.last)) return; | |
661 HPhi otherPhi = otherJoin.phis.first; | |
662 if (thenInput != otherPhi) return; | |
663 if (elseInput != otherPhi.inputs[1]) return; | |
664 } | |
665 if (hasAnyStatement(thenBlock, otherIf)) return; | |
666 } else { | |
667 if (!identical(end.predecessors[0], thenBlock)) return; | |
668 if (hasAnyStatement(thenBlock, thenInput)) return; | |
669 assert(thenBlock.successors.length == 1); | |
670 } | |
671 | |
672 // From now on, we have recognized a control flow operation built from | |
673 // the builder. Mark the if instruction as such. | |
674 controlFlowOperators.add(startIf); | |
675 | |
676 // Find the next non-HGoto instruction following the phi. | |
677 HInstruction nextInstruction = phi.block.first; | |
678 while (nextInstruction is HGoto) { | |
679 nextInstruction = nextInstruction.block.successors[0].first; | |
680 } | |
681 | |
682 // If the operation is only used by the first instruction | |
683 // of its block and is safe to be generated at use site, mark it | |
684 // so. | |
685 if (phi.usedBy.length == 1 | |
686 && phi.usedBy[0] == nextInstruction | |
687 && isSafeToGenerateAtUseSite(phi.usedBy[0], phi)) { | |
688 markAsGenerateAtUseSite(phi); | |
689 } | |
690 | |
691 if (identical(elseInput.block, elseBlock)) { | |
692 assert(elseInput.usedBy.length == 1); | |
693 markAsGenerateAtUseSite(elseInput); | |
694 } | |
695 | |
696 // If [thenInput] is defined in the first predecessor, then it is only used | |
697 // by [phi] and can be generated at use site. | |
698 if (identical(thenInput.block, end.predecessors[0])) { | |
699 assert(thenInput.usedBy.length == 1); | |
700 markAsGenerateAtUseSite(thenInput); | |
701 } | |
702 } | |
703 } | |
OLD | NEW |