| OLD | NEW |
| 1 library dart2js.cps_ir.backward_null_check_remover; | 1 library dart2js.cps_ir.backward_null_check_remover; |
| 2 | 2 |
| 3 import 'cps_ir_nodes.dart'; | 3 import 'cps_ir_nodes.dart'; |
| 4 import 'optimizers.dart'; | 4 import 'optimizers.dart'; |
| 5 import '../common/names.dart'; | 5 import '../common/names.dart'; |
| 6 import '../universe/selector.dart'; | 6 import '../universe/selector.dart'; |
| 7 import 'type_mask_system.dart'; | 7 import 'type_mask_system.dart'; |
| 8 import 'cps_fragment.dart'; | 8 import 'cps_fragment.dart'; |
| 9 | 9 |
| 10 /// Removes null checks that are follwed by another instruction that will | 10 /// Removes null checks that are follwed by another instruction that will |
| 11 /// perform the same check. | 11 /// perform the same check. |
| 12 /// | 12 /// |
| 13 /// For example: | 13 /// For example: |
| 14 /// | 14 /// |
| 15 /// x.toString; // NullCheck instruction | 15 /// x.toString; // NullCheck instruction |
| 16 /// print(x.length); | 16 /// print(x.length); |
| 17 /// | 17 /// |
| 18 /// ==> | 18 /// ==> |
| 19 /// | 19 /// |
| 20 /// print(x.length); | 20 /// print(x.length); |
| 21 /// | 21 /// |
| 22 /// `x.length` will throw when x is null, so the original [NullCheck] is not | 22 /// `x.length` will throw when x is null, so the original [ReceiverCheck] is not |
| 23 /// needed. This changes the error message, but at least for now we are | 23 /// needed. This changes the error message, but at least for now we are |
| 24 /// willing to accept this. | 24 /// willing to accept this. |
| 25 /// | 25 /// |
| 26 /// Note that code motion may not occur after this pass, since the [NullCheck] | 26 /// Note that code motion may not occur after this pass, since the [ReceiverChec
k] |
| 27 /// nodes are not there to restrict it. | 27 /// nodes are not there to restrict it. |
| 28 // | 28 // |
| 29 // TODO(asgerf): It would be nice with a clear specification of when we allow | 29 // TODO(asgerf): It would be nice with a clear specification of when we allow |
| 30 // the wording of error message to change. E.g. "toString" is already pretty | 30 // the wording of error message to change. E.g. "toString" is already pretty |
| 31 // bad so changing that should be ok, but changing a field access is not as | 31 // bad so changing that should be ok, but changing a field access is not as |
| 32 // clear. | 32 // clear. |
| 33 // | 33 // |
| 34 class BackwardNullCheckRemover extends BlockVisitor implements Pass { | 34 class BackwardNullCheckRemover extends BlockVisitor implements Pass { |
| 35 String get passName => 'Backward null-check remover'; | 35 String get passName => 'Backward null-check remover'; |
| 36 | 36 |
| 37 final TypeMaskSystem typeSystem; | 37 final TypeMaskSystem typeSystem; |
| 38 | 38 |
| 39 /// When the analysis of an expression completes, [nullCheckValue] refers to | 39 /// When the analysis of an expression completes, [nullCheckValue] refers to |
| 40 /// a value that is checked in the beginning of that expression. | 40 /// a value that is checked in the beginning of that expression. |
| 41 Primitive nullCheckedValue; | 41 Primitive nullCheckedValue; |
| 42 | 42 |
| 43 /// The [nullCheckedValue] at the entry point of a continuation. | 43 /// The [nullCheckedValue] at the entry point of a continuation. |
| 44 final Map<Continuation, Primitive> nullCheckedValueAt = | 44 final Map<Continuation, Primitive> nullCheckedValueAt = |
| 45 <Continuation, Primitive>{}; | 45 <Continuation, Primitive>{}; |
| 46 | 46 |
| 47 BackwardNullCheckRemover(this.typeSystem); | 47 BackwardNullCheckRemover(this.typeSystem); |
| 48 | 48 |
| 49 void rewrite(FunctionDefinition node) { | 49 void rewrite(FunctionDefinition node) { |
| 50 BlockVisitor.traverseInPostOrder(node, this); | 50 BlockVisitor.traverseInPostOrder(node, this); |
| 51 } | 51 } |
| 52 | 52 |
| 53 /// Returns a reference to an operand of [prim], where [prim] throws if null | 53 /// Returns a reference to an operand of [prim], where [prim] throws if null |
| 54 /// is passed into that operand. | 54 /// is passed into that operand. |
| 55 Reference<Primitive> getNullCheckedOperand(Primitive prim) { | 55 Reference<Primitive> getNullCheckedOperand(Primitive prim) { |
| 56 if (prim is NullCheck) return prim.value; | 56 if (prim is ReceiverCheck) return prim.value; |
| 57 if (prim is GetLength) return prim.object; | 57 if (prim is GetLength) return prim.object; |
| 58 if (prim is GetField) return prim.object; | 58 if (prim is GetField) return prim.object; |
| 59 if (prim is GetIndex) return prim.object; | 59 if (prim is GetIndex) return prim.object; |
| 60 if (prim is SetField) return prim.object; | 60 if (prim is SetField) return prim.object; |
| 61 if (prim is SetIndex) return prim.object; | 61 if (prim is SetIndex) return prim.object; |
| 62 if (prim is InvokeMethod && !selectorsOnNull.contains(prim.selector)) { | 62 if (prim is InvokeMethod && !selectorsOnNull.contains(prim.selector)) { |
| 63 return prim.dartReceiverReference; | 63 return prim.dartReceiverReference; |
| 64 } | 64 } |
| 65 if (prim is ForeignCode) { | 65 if (prim is ForeignCode) { |
| 66 return prim.isNullGuardOnNullFirstArgument() ? prim.arguments[0] : null; | 66 return prim.isNullGuardOnNullFirstArgument() ? prim.arguments[0] : null; |
| 67 } | 67 } |
| 68 return null; | 68 return null; |
| 69 } | 69 } |
| 70 | 70 |
| 71 /// It has been determined that the null check in [prim] made redundant by | 71 /// It has been determined that the null check in [prim] made redundant by |
| 72 /// [newNullCheck]. Eliminate [prim] if it is not needed any more. | 72 /// [newNullCheck]. Eliminate [prim] if it is not needed any more. |
| 73 void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { | 73 void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { |
| 74 if (prim is NullCheck) { | 74 if (prim is ReceiverCheck && prim.isNullCheck) { |
| 75 Primitive value = prim.value.definition; | 75 Primitive value = prim.value.definition; |
| 76 LetPrim let = prim.parent; | 76 LetPrim let = prim.parent; |
| 77 prim..replaceUsesWith(value)..destroy(); | 77 prim..replaceUsesWith(value)..destroy(); |
| 78 let.remove(); | 78 let.remove(); |
| 79 } else if (prim is GetLength || prim is GetField || prim is GetIndex) { | 79 } else if (prim is GetLength || prim is GetField || prim is GetIndex) { |
| 80 if (prim.hasNoRefinedUses) { | 80 if (prim.hasNoRefinedUses) { |
| 81 destroyRefinementsOfDeadPrimitive(prim); | 81 destroyRefinementsOfDeadPrimitive(prim); |
| 82 LetPrim let = prim.parent; | 82 LetPrim let = prim.parent; |
| 83 prim..destroy(); | 83 prim..destroy(); |
| 84 let.remove(); | 84 let.remove(); |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 120 void visitLetHandler(LetHandler node) { | 120 void visitLetHandler(LetHandler node) { |
| 121 nullCheckedValue = null; | 121 nullCheckedValue = null; |
| 122 } | 122 } |
| 123 | 123 |
| 124 visitInvokeContinuation(InvokeContinuation node) { | 124 visitInvokeContinuation(InvokeContinuation node) { |
| 125 if (!node.isRecursive) { | 125 if (!node.isRecursive) { |
| 126 nullCheckedValue = nullCheckedValueAt[node.continuation.definition]; | 126 nullCheckedValue = nullCheckedValueAt[node.continuation.definition]; |
| 127 } | 127 } |
| 128 } | 128 } |
| 129 } | 129 } |
| OLD | NEW |