| 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 |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 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 an operand of [prim] that throws if null is passed into it. |
| 54 /// is passed into that operand. | 54 Primitive getNullCheckedOperand(Primitive prim) { |
| 55 Reference<Primitive> getNullCheckedOperand(Primitive prim) { | |
| 56 if (prim is ReceiverCheck) return prim.value; | 55 if (prim is ReceiverCheck) return prim.value; |
| 57 if (prim is GetLength) return prim.object; | 56 if (prim is GetLength) return prim.object; |
| 58 if (prim is GetField) return prim.object; | 57 if (prim is GetField) return prim.object; |
| 59 if (prim is GetIndex) return prim.object; | 58 if (prim is GetIndex) return prim.object; |
| 60 if (prim is SetField) return prim.object; | 59 if (prim is SetField) return prim.object; |
| 61 if (prim is SetIndex) return prim.object; | 60 if (prim is SetIndex) return prim.object; |
| 62 if (prim is InvokeMethod && !selectorsOnNull.contains(prim.selector)) { | 61 if (prim is InvokeMethod && !selectorsOnNull.contains(prim.selector)) { |
| 63 return prim.dartReceiverReference; | 62 return prim.dartReceiver; |
| 64 } | 63 } |
| 65 if (prim is ForeignCode) { | 64 if (prim is ForeignCode) { |
| 66 return prim.isNullGuardOnNullFirstArgument() ? prim.arguments[0] : null; | 65 return prim.isNullGuardOnNullFirstArgument() ? prim.argument(0) : null; |
| 67 } | 66 } |
| 68 return null; | 67 return null; |
| 69 } | 68 } |
| 70 | 69 |
| 71 /// It has been determined that the null check in [prim] made redundant by | 70 /// It has been determined that the null check in [prim] made redundant by |
| 72 /// [newNullCheck]. Eliminate [prim] if it is not needed any more. | 71 /// [newNullCheck]. Eliminate [prim] if it is not needed any more. |
| 73 void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { | 72 void tryEliminateRedundantNullCheck(Primitive prim, Primitive newNullCheck) { |
| 74 if (prim is ReceiverCheck && prim.isNullCheck) { | 73 if (prim is ReceiverCheck && prim.isNullCheck) { |
| 75 Primitive value = prim.value.definition; | 74 Primitive value = prim.value; |
| 76 LetPrim let = prim.parent; | 75 LetPrim let = prim.parent; |
| 77 prim..replaceUsesWith(value)..destroy(); | 76 prim..replaceUsesWith(value)..destroy(); |
| 78 let.remove(); | 77 let.remove(); |
| 79 } else if (prim is GetLength || prim is GetField || prim is GetIndex) { | 78 } else if (prim is GetLength || prim is GetField || prim is GetIndex) { |
| 80 if (prim.hasNoRefinedUses) { | 79 if (prim.hasNoRefinedUses) { |
| 81 destroyRefinementsOfDeadPrimitive(prim); | 80 destroyRefinementsOfDeadPrimitive(prim); |
| 82 LetPrim let = prim.parent; | 81 LetPrim let = prim.parent; |
| 83 prim..destroy(); | 82 prim..destroy(); |
| 84 let.remove(); | 83 let.remove(); |
| 85 } | 84 } |
| 86 } | 85 } |
| 87 } | 86 } |
| 88 | 87 |
| 89 /// True if [prim] can be moved above a null check. This is safe if [prim] | 88 /// True if [prim] can be moved above a null check. This is safe if [prim] |
| 90 /// cannot throw or have side effects and does not carry any path-sensitive | 89 /// cannot throw or have side effects and does not carry any path-sensitive |
| 91 /// type information, such as [Refinement] nodes do. | 90 /// type information, such as [Refinement] nodes do. |
| 92 // | 91 // |
| 93 // TODO(asgerf): This prevents elimination of the .length created for a bounds | 92 // TODO(asgerf): This prevents elimination of the .length created for a bounds |
| 94 // check, because there is a refinement node below it. To handle this, we | 93 // check, because there is a refinement node below it. To handle this, we |
| 95 // would have to relocate the [Refinement] node below the new null check. | 94 // would have to relocate the [Refinement] node below the new null check. |
| 96 bool canMoveAboveNullCheck(Primitive prim) { | 95 bool canMoveAboveNullCheck(Primitive prim) { |
| 97 return prim.isSafeForReordering; | 96 return prim.isSafeForReordering; |
| 98 } | 97 } |
| 99 | 98 |
| 100 void visitLetPrim(LetPrim node) { | 99 void visitLetPrim(LetPrim node) { |
| 101 Primitive prim = node.primitive; | 100 Primitive prim = node.primitive; |
| 102 Primitive receiver = getNullCheckedOperand(prim)?.definition; | 101 Primitive receiver = getNullCheckedOperand(prim); |
| 103 if (receiver != null) { | 102 if (receiver != null) { |
| 104 if (nullCheckedValue != null && receiver.sameValue(nullCheckedValue)) { | 103 if (nullCheckedValue != null && receiver.sameValue(nullCheckedValue)) { |
| 105 tryEliminateRedundantNullCheck(prim, nullCheckedValue); | 104 tryEliminateRedundantNullCheck(prim, nullCheckedValue); |
| 106 } | 105 } |
| 107 nullCheckedValue = receiver; | 106 nullCheckedValue = receiver; |
| 108 } else if (!canMoveAboveNullCheck(prim)) { | 107 } else if (!canMoveAboveNullCheck(prim)) { |
| 109 nullCheckedValue = null; | 108 nullCheckedValue = null; |
| 110 } | 109 } |
| 111 } | 110 } |
| 112 | 111 |
| 113 void visitContinuation(Continuation cont) { | 112 void visitContinuation(Continuation cont) { |
| 114 if (nullCheckedValue != null) { | 113 if (nullCheckedValue != null) { |
| 115 nullCheckedValueAt[cont] = nullCheckedValue; | 114 nullCheckedValueAt[cont] = nullCheckedValue; |
| 116 nullCheckedValue = null; | 115 nullCheckedValue = null; |
| 117 } | 116 } |
| 118 } | 117 } |
| 119 | 118 |
| 120 void visitLetHandler(LetHandler node) { | 119 void visitLetHandler(LetHandler node) { |
| 121 nullCheckedValue = null; | 120 nullCheckedValue = null; |
| 122 } | 121 } |
| 123 | 122 |
| 124 visitInvokeContinuation(InvokeContinuation node) { | 123 visitInvokeContinuation(InvokeContinuation node) { |
| 125 if (!node.isRecursive) { | 124 if (!node.isRecursive) { |
| 126 nullCheckedValue = nullCheckedValueAt[node.continuation.definition]; | 125 nullCheckedValue = nullCheckedValueAt[node.continuation]; |
| 127 } | 126 } |
| 128 } | 127 } |
| 129 } | 128 } |
| OLD | NEW |