| OLD | NEW |
| (Empty) |
| 1 library dart2js.cps_ir.finalize; | |
| 2 | |
| 3 import '../js/js.dart' as js; | |
| 4 import '../js_backend/backend_helpers.dart'; | |
| 5 import '../js_backend/js_backend.dart' show JavaScriptBackend; | |
| 6 import 'cps_fragment.dart'; | |
| 7 import 'cps_ir_nodes.dart'; | |
| 8 import 'optimizers.dart' show Pass; | |
| 9 | |
| 10 /// A transformation pass that must run immediately before the tree IR builder. | |
| 11 /// | |
| 12 /// This expands [BoundsCheck] nodes into more low-level operations. | |
| 13 class Finalize extends TrampolineRecursiveVisitor implements Pass { | |
| 14 String get passName => 'Finalize'; | |
| 15 | |
| 16 JavaScriptBackend backend; | |
| 17 BackendHelpers get helpers => backend.helpers; | |
| 18 | |
| 19 Finalize(this.backend); | |
| 20 | |
| 21 void rewrite(FunctionDefinition node) { | |
| 22 visit(node); | |
| 23 } | |
| 24 | |
| 25 Expression traverseLetPrim(LetPrim node) { | |
| 26 CpsFragment cps = visit(node.primitive); | |
| 27 if (cps == null) return node.body; | |
| 28 cps.insertBelow(node); | |
| 29 Expression next = node.body; | |
| 30 node.remove(); | |
| 31 return next; | |
| 32 } | |
| 33 | |
| 34 bool areAdjacent(Primitive first, Primitive second) { | |
| 35 return first.parent == second.parent.parent; | |
| 36 } | |
| 37 | |
| 38 CpsFragment visitBoundsCheck(BoundsCheck node) { | |
| 39 CpsFragment cps = new CpsFragment(node.sourceInformation); | |
| 40 if (node.hasNoChecks) { | |
| 41 node | |
| 42 ..replaceUsesWith(node.object) | |
| 43 ..destroy(); | |
| 44 return cps; | |
| 45 } | |
| 46 Continuation fail = cps.letCont(); | |
| 47 Primitive index = node.index; | |
| 48 if (node.hasIntegerCheck) { | |
| 49 cps | |
| 50 .ifTruthy(cps.applyBuiltin( | |
| 51 BuiltinOperator.IsNotUnsigned32BitInteger, [index, index])) | |
| 52 .invokeContinuation(fail); | |
| 53 } else if (node.hasLowerBoundCheck) { | |
| 54 cps | |
| 55 .ifTruthy( | |
| 56 cps.applyBuiltin(BuiltinOperator.NumLt, [index, cps.makeZero()])) | |
| 57 .invokeContinuation(fail); | |
| 58 } | |
| 59 if (node.hasUpperBoundCheck) { | |
| 60 Primitive length = node.length; | |
| 61 if (length is GetLength && | |
| 62 length.hasExactlyOneUse && | |
| 63 areAdjacent(length, node)) { | |
| 64 // Rebind the GetLength here, so it does not get stuck outside the | |
| 65 // condition, blocked from propagating by the lower bounds check. | |
| 66 LetPrim lengthBinding = length.parent; | |
| 67 lengthBinding.remove(); | |
| 68 cps.letPrim(length); | |
| 69 } | |
| 70 cps | |
| 71 .ifTruthy(cps.applyBuiltin(BuiltinOperator.NumGe, [index, length])) | |
| 72 .invokeContinuation(fail); | |
| 73 } | |
| 74 if (node.hasEmptinessCheck) { | |
| 75 cps | |
| 76 .ifTruthy(cps.applyBuiltin( | |
| 77 BuiltinOperator.StrictEq, [node.length, cps.makeZero()])) | |
| 78 .invokeContinuation(fail); | |
| 79 } | |
| 80 cps.insideContinuation(fail).invokeStaticThrower( | |
| 81 helpers.throwIndexOutOfRangeException, [node.object, index]); | |
| 82 node | |
| 83 ..replaceUsesWith(node.object) | |
| 84 ..destroy(); | |
| 85 return cps; | |
| 86 } | |
| 87 | |
| 88 void visitGetStatic(GetStatic node) { | |
| 89 if (node.witnessRef != null) { | |
| 90 node | |
| 91 ..witnessRef.unlink() | |
| 92 ..witnessRef = null; | |
| 93 } | |
| 94 } | |
| 95 | |
| 96 void visitForeignCode(ForeignCode node) { | |
| 97 if (js.isIdentityTemplate(node.codeTemplate)) { | |
| 98 // The CPS builder replaces identity templates with refinements, except | |
| 99 // when the refined type is an array type. Some optimizations assume the | |
| 100 // type of an object is immutable, but the type of an array can change | |
| 101 // after allocation. After the finalize pass, this assumption is no | |
| 102 // longer needed, so we can replace the remaining idenitity templates. | |
| 103 Refinement refinement = new Refinement(node.argument(0), node.type) | |
| 104 ..type = node.type; | |
| 105 node.replaceWith(refinement); | |
| 106 } | |
| 107 } | |
| 108 } | |
| OLD | NEW |