OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 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 | 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 import '../compiler.dart' show Compiler; | 5 import '../compiler.dart' show Compiler; |
6 import '../constants/values.dart'; | 6 import '../constants/values.dart'; |
7 import '../elements/elements.dart'; | 7 import '../elements/elements.dart'; |
8 import '../js_backend/js_backend.dart'; | 8 import '../js_backend/js_backend.dart'; |
9 import '../types/types.dart'; | 9 import '../types/types.dart'; |
10 import '../universe/selector.dart' show Selector; | 10 import '../universe/selector.dart' show Selector; |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
60 } | 60 } |
61 | 61 |
62 HInstruction visitInstruction(HInstruction node) { | 62 HInstruction visitInstruction(HInstruction node) { |
63 return node; | 63 return node; |
64 } | 64 } |
65 | 65 |
66 HInstruction visitIs(HIs node) { | 66 HInstruction visitIs(HIs node) { |
67 if (node.kind == HIs.RAW_CHECK) { | 67 if (node.kind == HIs.RAW_CHECK) { |
68 HInstruction interceptor = node.interceptor; | 68 HInstruction interceptor = node.interceptor; |
69 if (interceptor != null) { | 69 if (interceptor != null) { |
70 return new HIsViaInterceptor(node.typeExpression, interceptor, | 70 return new HIsViaInterceptor( |
71 backend.boolType); | 71 node.typeExpression, interceptor, backend.boolType); |
72 } | 72 } |
73 } | 73 } |
74 return node; | 74 return node; |
75 } | 75 } |
76 | 76 |
77 HInstruction visitIdentity(HIdentity node) { | 77 HInstruction visitIdentity(HIdentity node) { |
78 node.singleComparisonOp = simpleOp(node.left, node.right); | 78 node.singleComparisonOp = simpleOp(node.left, node.right); |
79 return node; | 79 return node; |
80 } | 80 } |
81 | 81 |
82 String simpleOp(HInstruction left, HInstruction right) { | 82 String simpleOp(HInstruction left, HInstruction right) { |
83 // Returns the single identity comparison (== or ===) or null if a more | 83 // Returns the single identity comparison (== or ===) or null if a more |
84 // complex expression is required. | 84 // complex expression is required. |
85 TypeMask leftType = left.instructionType; | 85 TypeMask leftType = left.instructionType; |
86 TypeMask rightType = right.instructionType; | 86 TypeMask rightType = right.instructionType; |
87 if (leftType.isNullable && rightType.isNullable) { | 87 if (leftType.isNullable && rightType.isNullable) { |
88 if (left.isConstantNull() || | 88 if (left.isConstantNull() || |
89 right.isConstantNull() || | 89 right.isConstantNull() || |
90 (left.isPrimitive(compiler) && | 90 (left.isPrimitive(compiler) && leftType == rightType)) { |
91 leftType == rightType)) { | |
92 return '=='; | 91 return '=='; |
93 } | 92 } |
94 return null; | 93 return null; |
95 } | 94 } |
96 return '==='; | 95 return '==='; |
97 } | 96 } |
98 | 97 |
99 HInstruction visitInvokeDynamic(HInvokeDynamic node) { | 98 HInstruction visitInvokeDynamic(HInvokeDynamic node) { |
100 if (node.isInterceptedCall) { | 99 if (node.isInterceptedCall) { |
101 // Calls of the form | 100 // Calls of the form |
(...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
186 HInstruction left = binary.left; | 185 HInstruction left = binary.left; |
187 HInstruction right = binary.right; | 186 HInstruction right = binary.right; |
188 if (isMatchingRead(left)) { | 187 if (isMatchingRead(left)) { |
189 if (left.usedBy.length == 1) { | 188 if (left.usedBy.length == 1) { |
190 if (right is HConstant && right.constant.isOne) { | 189 if (right is HConstant && right.constant.isOne) { |
191 HInstruction rmw = new HReadModifyWrite.preOp( | 190 HInstruction rmw = new HReadModifyWrite.preOp( |
192 setter.element, incrementOp, receiver, op.instructionType); | 191 setter.element, incrementOp, receiver, op.instructionType); |
193 return replaceOp(rmw, left); | 192 return replaceOp(rmw, left); |
194 } else { | 193 } else { |
195 HInstruction rmw = new HReadModifyWrite.assignOp( | 194 HInstruction rmw = new HReadModifyWrite.assignOp( |
196 setter.element, | 195 setter.element, assignOp, receiver, right, op.instructionType); |
197 assignOp, | |
198 receiver, right, op.instructionType); | |
199 return replaceOp(rmw, left); | 196 return replaceOp(rmw, left); |
200 } | 197 } |
201 } else if (op.usedBy.length == 1 && | 198 } else if (op.usedBy.length == 1 && |
202 right is HConstant && | 199 right is HConstant && |
203 right.constant.isOne) { | 200 right.constant.isOne) { |
204 HInstruction rmw = new HReadModifyWrite.postOp( | 201 HInstruction rmw = new HReadModifyWrite.postOp( |
205 setter.element, incrementOp, receiver, op.instructionType); | 202 setter.element, incrementOp, receiver, op.instructionType); |
206 block.addAfter(left, rmw); | 203 block.addAfter(left, rmw); |
207 block.remove(setter); | 204 block.remove(setter); |
208 block.remove(op); | 205 block.remove(op); |
209 block.rewrite(left, rmw); | 206 block.rewrite(left, rmw); |
210 block.remove(left); | 207 block.remove(left); |
211 return null; | 208 return null; |
212 } | 209 } |
213 } | 210 } |
214 return noMatchingRead(); | 211 return noMatchingRead(); |
215 } | 212 } |
216 | 213 |
217 HInstruction simple(String assignOp, | 214 HInstruction simple( |
218 HInstruction left, HInstruction right) { | 215 String assignOp, HInstruction left, HInstruction right) { |
219 if (isMatchingRead(left)) { | 216 if (isMatchingRead(left)) { |
220 if (left.usedBy.length == 1) { | 217 if (left.usedBy.length == 1) { |
221 HInstruction rmw = new HReadModifyWrite.assignOp( | 218 HInstruction rmw = new HReadModifyWrite.assignOp( |
222 setter.element, | 219 setter.element, assignOp, receiver, right, op.instructionType); |
223 assignOp, | |
224 receiver, right, op.instructionType); | |
225 return replaceOp(rmw, left); | 220 return replaceOp(rmw, left); |
226 } | 221 } |
227 } | 222 } |
228 return noMatchingRead(); | 223 return noMatchingRead(); |
229 } | 224 } |
230 | 225 |
231 HInstruction simpleBinary(String assignOp) { | 226 HInstruction simpleBinary(String assignOp) { |
232 HInvokeBinary binary = op; | 227 HInvokeBinary binary = op; |
233 return simple(assignOp, binary.left, binary.right); | 228 return simple(assignOp, binary.left, binary.right); |
234 } | 229 } |
(...skipping 19 matching lines...) Expand all Loading... |
254 | 249 |
255 return noMatchingRead(); | 250 return noMatchingRead(); |
256 } | 251 } |
257 } | 252 } |
258 | 253 |
259 /** | 254 /** |
260 * Remove [HTypeKnown] instructions from the graph, to make codegen | 255 * Remove [HTypeKnown] instructions from the graph, to make codegen |
261 * analysis easier. | 256 * analysis easier. |
262 */ | 257 */ |
263 class SsaTypeKnownRemover extends HBaseVisitor { | 258 class SsaTypeKnownRemover extends HBaseVisitor { |
264 | |
265 void visitGraph(HGraph graph) { | 259 void visitGraph(HGraph graph) { |
266 visitDominatorTree(graph); | 260 visitDominatorTree(graph); |
267 } | 261 } |
268 | 262 |
269 void visitBasicBlock(HBasicBlock block) { | 263 void visitBasicBlock(HBasicBlock block) { |
270 HInstruction instruction = block.first; | 264 HInstruction instruction = block.first; |
271 while (instruction != null) { | 265 while (instruction != null) { |
272 HInstruction next = instruction.next; | 266 HInstruction next = instruction.next; |
273 instruction.accept(this); | 267 instruction.accept(this); |
274 instruction = next; | 268 instruction = next; |
275 } | 269 } |
276 } | 270 } |
277 | 271 |
278 void visitTypeKnown(HTypeKnown instruction) { | 272 void visitTypeKnown(HTypeKnown instruction) { |
279 instruction.block.rewrite(instruction, instruction.checkedInput); | 273 instruction.block.rewrite(instruction, instruction.checkedInput); |
280 instruction.block.remove(instruction); | 274 instruction.block.remove(instruction); |
281 } | 275 } |
282 } | 276 } |
283 | 277 |
284 /** | 278 /** |
285 * Remove [HTypeConversion] instructions from the graph in '--trust-primitives' | 279 * Remove [HTypeConversion] instructions from the graph in '--trust-primitives' |
286 * mode. | 280 * mode. |
287 */ | 281 */ |
288 class SsaTrustedCheckRemover extends HBaseVisitor { | 282 class SsaTrustedCheckRemover extends HBaseVisitor { |
289 | |
290 Compiler compiler; | 283 Compiler compiler; |
291 SsaTrustedCheckRemover(this.compiler); | 284 SsaTrustedCheckRemover(this.compiler); |
292 | 285 |
293 void visitGraph(HGraph graph) { | 286 void visitGraph(HGraph graph) { |
294 if (!compiler.options.trustPrimitives) return; | 287 if (!compiler.options.trustPrimitives) return; |
295 visitDominatorTree(graph); | 288 visitDominatorTree(graph); |
296 } | 289 } |
297 | 290 |
298 void visitBasicBlock(HBasicBlock block) { | 291 void visitBasicBlock(HBasicBlock block) { |
299 HInstruction instruction = block.first; | 292 HInstruction instruction = block.first; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
347 JavaScriptBackend get backend => compiler.backend; | 340 JavaScriptBackend get backend => compiler.backend; |
348 | 341 |
349 void visitGraph(HGraph graph) { | 342 void visitGraph(HGraph graph) { |
350 visitDominatorTree(graph); | 343 visitDominatorTree(graph); |
351 } | 344 } |
352 | 345 |
353 void analyzeInputs(HInstruction user, int start) { | 346 void analyzeInputs(HInstruction user, int start) { |
354 List<HInstruction> inputs = user.inputs; | 347 List<HInstruction> inputs = user.inputs; |
355 for (int i = start; i < inputs.length; i++) { | 348 for (int i = start; i < inputs.length; i++) { |
356 HInstruction input = inputs[i]; | 349 HInstruction input = inputs[i]; |
357 if (!generateAtUseSite.contains(input) | 350 if (!generateAtUseSite.contains(input) && |
358 && !input.isCodeMotionInvariant() | 351 !input.isCodeMotionInvariant() && |
359 && input.usedBy.length == 1 | 352 input.usedBy.length == 1 && |
360 && input is !HPhi | 353 input is! HPhi && |
361 && input is !HLocalValue | 354 input is! HLocalValue && |
362 && !input.isJsStatement()) { | 355 !input.isJsStatement()) { |
363 if (input.isPure()) { | 356 if (input.isPure()) { |
364 // Only consider a pure input if it is in the same loop. | 357 // Only consider a pure input if it is in the same loop. |
365 // Otherwise, we might move GVN'ed instruction back into the | 358 // Otherwise, we might move GVN'ed instruction back into the |
366 // loop. | 359 // loop. |
367 if (user.hasSameLoopHeaderAs(input)) { | 360 if (user.hasSameLoopHeaderAs(input)) { |
368 // Move it closer to [user], so that instructions in | 361 // Move it closer to [user], so that instructions in |
369 // between do not prevent making it generate at use site. | 362 // between do not prevent making it generate at use site. |
370 input.moveBefore(user); | 363 input.moveBefore(user); |
371 pureInputs.add(input); | 364 pureInputs.add(input); |
372 // Previous computations done on [input] are now invalid | 365 // Previous computations done on [input] are now invalid |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
434 // does not require an expression with multiple uses (because of null / | 427 // does not require an expression with multiple uses (because of null / |
435 // undefined). | 428 // undefined). |
436 void visitIdentity(HIdentity instruction) { | 429 void visitIdentity(HIdentity instruction) { |
437 if (instruction.singleComparisonOp != null) { | 430 if (instruction.singleComparisonOp != null) { |
438 super.visitIdentity(instruction); | 431 super.visitIdentity(instruction); |
439 } | 432 } |
440 // Do nothing. | 433 // Do nothing. |
441 } | 434 } |
442 | 435 |
443 void visitTypeConversion(HTypeConversion instruction) { | 436 void visitTypeConversion(HTypeConversion instruction) { |
444 if (!instruction.isArgumentTypeCheck | 437 if (!instruction.isArgumentTypeCheck && !instruction.isReceiverTypeCheck) { |
445 && !instruction.isReceiverTypeCheck) { | |
446 assert(instruction.isCheckedModeCheck || instruction.isCastTypeCheck); | 438 assert(instruction.isCheckedModeCheck || instruction.isCastTypeCheck); |
447 // Checked mode checks and cast checks compile to code that | 439 // Checked mode checks and cast checks compile to code that |
448 // only use their input once, so we can safely visit them | 440 // only use their input once, so we can safely visit them |
449 // and try to merge the input. | 441 // and try to merge the input. |
450 visitInstruction(instruction); | 442 visitInstruction(instruction); |
451 } | 443 } |
452 } | 444 } |
453 | 445 |
454 void visitTypeKnown(HTypeKnown instruction) { | 446 void visitTypeKnown(HTypeKnown instruction) { |
455 // [HTypeKnown] instructions are removed before code generation. | 447 // [HTypeKnown] instructions are removed before code generation. |
456 assert(false); | 448 assert(false); |
457 } | 449 } |
458 | 450 |
459 void tryGenerateAtUseSite(HInstruction instruction) { | 451 void tryGenerateAtUseSite(HInstruction instruction) { |
460 if (instruction.isControlFlow()) return; | 452 if (instruction.isControlFlow()) return; |
461 markAsGenerateAtUseSite(instruction); | 453 markAsGenerateAtUseSite(instruction); |
462 } | 454 } |
463 | 455 |
464 bool isBlockSinglePredecessor(HBasicBlock block) { | 456 bool isBlockSinglePredecessor(HBasicBlock block) { |
465 return block.successors.length == 1 | 457 return block.successors.length == 1 && |
466 && block.successors[0].predecessors.length == 1; | 458 block.successors[0].predecessors.length == 1; |
467 } | 459 } |
468 | 460 |
469 void visitBasicBlock(HBasicBlock block) { | 461 void visitBasicBlock(HBasicBlock block) { |
470 // Compensate from not merging blocks: if the block is the | 462 // Compensate from not merging blocks: if the block is the |
471 // single predecessor of its single successor, let the successor | 463 // single predecessor of its single successor, let the successor |
472 // visit it. | 464 // visit it. |
473 if (isBlockSinglePredecessor(block)) return; | 465 if (isBlockSinglePredecessor(block)) return; |
474 | 466 |
475 tryMergingExpressions(block); | 467 tryMergingExpressions(block); |
476 } | 468 } |
(...skipping 19 matching lines...) Expand all Loading... |
496 assert(nextInput.usedBy.length == 1); | 488 assert(nextInput.usedBy.length == 1); |
497 if (identical(nextInput, instruction)) { | 489 if (identical(nextInput, instruction)) { |
498 return true; | 490 return true; |
499 } | 491 } |
500 } | 492 } |
501 return false; | 493 return false; |
502 } | 494 } |
503 | 495 |
504 block.last.accept(this); | 496 block.last.accept(this); |
505 for (HInstruction instruction = block.last.previous; | 497 for (HInstruction instruction = block.last.previous; |
506 instruction != null; | 498 instruction != null; |
507 instruction = instruction.previous) { | 499 instruction = instruction.previous) { |
508 if (generateAtUseSite.contains(instruction)) { | 500 if (generateAtUseSite.contains(instruction)) { |
509 continue; | 501 continue; |
510 } | 502 } |
511 if (instruction.isCodeMotionInvariant()) { | 503 if (instruction.isCodeMotionInvariant()) { |
512 markAsGenerateAtUseSite(instruction); | 504 markAsGenerateAtUseSite(instruction); |
513 continue; | 505 continue; |
514 } | 506 } |
515 if (instruction.isPure()) { | 507 if (instruction.isPure()) { |
516 if (pureInputs.contains(instruction)) { | 508 if (pureInputs.contains(instruction)) { |
517 tryGenerateAtUseSite(instruction); | 509 tryGenerateAtUseSite(instruction); |
(...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
578 // The current instruction is the next non-trivial | 570 // The current instruction is the next non-trivial |
579 // expected input. | 571 // expected input. |
580 tryGenerateAtUseSite(instruction); | 572 tryGenerateAtUseSite(instruction); |
581 } else { | 573 } else { |
582 assert(expectedInputs.isEmpty); | 574 assert(expectedInputs.isEmpty); |
583 } | 575 } |
584 instruction.accept(this); | 576 instruction.accept(this); |
585 } | 577 } |
586 } | 578 } |
587 | 579 |
588 if (block.predecessors.length == 1 | 580 if (block.predecessors.length == 1 && |
589 && isBlockSinglePredecessor(block.predecessors[0])) { | 581 isBlockSinglePredecessor(block.predecessors[0])) { |
590 assert(block.phis.isEmpty); | 582 assert(block.phis.isEmpty); |
591 tryMergingExpressions(block.predecessors[0]); | 583 tryMergingExpressions(block.predecessors[0]); |
592 } else { | 584 } else { |
593 expectedInputs = null; | 585 expectedInputs = null; |
594 pureInputs = null; | 586 pureInputs = null; |
595 } | 587 } |
596 } | 588 } |
597 } | 589 } |
598 | 590 |
599 /** | 591 /** |
(...skipping 23 matching lines...) Expand all Loading... |
623 bool hasAnyStatement(HBasicBlock block, HInstruction instruction) { | 615 bool hasAnyStatement(HBasicBlock block, HInstruction instruction) { |
624 // If [instruction] is not in [block], then if the block is not | 616 // If [instruction] is not in [block], then if the block is not |
625 // empty, we know there will be a statement to emit. | 617 // empty, we know there will be a statement to emit. |
626 if (!identical(instruction.block, block)) { | 618 if (!identical(instruction.block, block)) { |
627 return !identical(block.last, block.first); | 619 return !identical(block.last, block.first); |
628 } | 620 } |
629 | 621 |
630 // If [instruction] is not the last instruction of the block | 622 // If [instruction] is not the last instruction of the block |
631 // before the control flow instruction, or the last instruction, | 623 // before the control flow instruction, or the last instruction, |
632 // then we will have to emit a statement for that last instruction. | 624 // then we will have to emit a statement for that last instruction. |
633 if (instruction != block.last | 625 if (instruction != block.last && |
634 && !identical(instruction, block.last.previous)) return true; | 626 !identical(instruction, block.last.previous)) return true; |
635 | 627 |
636 // If one of the instructions in the block until [instruction] is | 628 // If one of the instructions in the block until [instruction] is |
637 // not generated at use site, then we will have to emit a | 629 // not generated at use site, then we will have to emit a |
638 // statement for it. | 630 // statement for it. |
639 // TODO(ngeoffray): we could generate a comma separated | 631 // TODO(ngeoffray): we could generate a comma separated |
640 // list of expressions. | 632 // list of expressions. |
641 for (HInstruction temp = block.first; | 633 for (HInstruction temp = block.first; |
642 !identical(temp, instruction); | 634 !identical(temp, instruction); |
643 temp = temp.next) { | 635 temp = temp.next) { |
644 if (!generateAtUseSite.contains(temp)) return true; | 636 if (!generateAtUseSite.contains(temp)) return true; |
645 } | 637 } |
646 | 638 |
647 return false; | 639 return false; |
648 } | 640 } |
649 | 641 |
650 bool isSafeToGenerateAtUseSite(HInstruction user, HInstruction input) { | 642 bool isSafeToGenerateAtUseSite(HInstruction user, HInstruction input) { |
651 // HForeignNew evaluates arguments in order and passes them to a | 643 // HForeignNew evaluates arguments in order and passes them to a |
652 // constructor. | 644 // constructor. |
653 if (user is HForeignNew) return true; | 645 if (user is HForeignNew) return true; |
654 // A [HForeign] instruction uses operators and if we generate | 646 // A [HForeign] instruction uses operators and if we generate |
655 // [input] at use site, the precedence might be wrong. | 647 // [input] at use site, the precedence might be wrong. |
656 if (user is HForeign) return false; | 648 if (user is HForeign) return false; |
657 // A [HCheck] instruction with control flow uses its input | 649 // A [HCheck] instruction with control flow uses its input |
658 // multiple times, so we avoid generating it at use site. | 650 // multiple times, so we avoid generating it at use site. |
659 if (user is HCheck && user.isControlFlow()) return false; | 651 if (user is HCheck && user.isControlFlow()) return false; |
660 // A [HIs] instruction uses its input multiple times, so we | 652 // A [HIs] instruction uses its input multiple times, so we |
661 // avoid generating it at use site. | 653 // avoid generating it at use site. |
662 if (user is HIs) return false; | 654 if (user is HIs) return false; |
663 return true; | 655 return true; |
664 } | 656 } |
665 | 657 |
666 void visitBasicBlock(HBasicBlock block) { | 658 void visitBasicBlock(HBasicBlock block) { |
667 if (block.last is !HIf) return; | 659 if (block.last is! HIf) return; |
668 HIf startIf = block.last; | 660 HIf startIf = block.last; |
669 HBasicBlock end = startIf.joinBlock; | 661 HBasicBlock end = startIf.joinBlock; |
670 | 662 |
671 // We check that the structure is the following: | 663 // We check that the structure is the following: |
672 // If | 664 // If |
673 // / \ | 665 // / \ |
674 // / \ | 666 // / \ |
675 // 1 expr goto | 667 // 1 expr goto |
676 // goto / | 668 // goto / |
677 // \ / | 669 // \ / |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
747 | 739 |
748 // Find the next non-HGoto instruction following the phi. | 740 // Find the next non-HGoto instruction following the phi. |
749 HInstruction nextInstruction = phi.block.first; | 741 HInstruction nextInstruction = phi.block.first; |
750 while (nextInstruction is HGoto) { | 742 while (nextInstruction is HGoto) { |
751 nextInstruction = nextInstruction.block.successors[0].first; | 743 nextInstruction = nextInstruction.block.successors[0].first; |
752 } | 744 } |
753 | 745 |
754 // If the operation is only used by the first instruction | 746 // If the operation is only used by the first instruction |
755 // of its block and is safe to be generated at use site, mark it | 747 // of its block and is safe to be generated at use site, mark it |
756 // so. | 748 // so. |
757 if (phi.usedBy.length == 1 | 749 if (phi.usedBy.length == 1 && |
758 && phi.usedBy[0] == nextInstruction | 750 phi.usedBy[0] == nextInstruction && |
759 && isSafeToGenerateAtUseSite(phi.usedBy[0], phi)) { | 751 isSafeToGenerateAtUseSite(phi.usedBy[0], phi)) { |
760 markAsGenerateAtUseSite(phi); | 752 markAsGenerateAtUseSite(phi); |
761 } | 753 } |
762 | 754 |
763 if (identical(elseInput.block, elseBlock)) { | 755 if (identical(elseInput.block, elseBlock)) { |
764 assert(elseInput.usedBy.length == 1); | 756 assert(elseInput.usedBy.length == 1); |
765 markAsGenerateAtUseSite(elseInput); | 757 markAsGenerateAtUseSite(elseInput); |
766 } | 758 } |
767 | 759 |
768 // If [thenInput] is defined in the first predecessor, then it is only used | 760 // If [thenInput] is defined in the first predecessor, then it is only used |
769 // by [phi] and can be generated at use site. | 761 // by [phi] and can be generated at use site. |
770 if (identical(thenInput.block, end.predecessors[0])) { | 762 if (identical(thenInput.block, end.predecessors[0])) { |
771 assert(thenInput.usedBy.length == 1); | 763 assert(thenInput.usedBy.length == 1); |
772 markAsGenerateAtUseSite(thenInput); | 764 markAsGenerateAtUseSite(thenInput); |
773 } | 765 } |
774 } | 766 } |
775 } | 767 } |
OLD | NEW |