| 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 part of ssa; | 5 part of ssa; |
| 6 | 6 |
| 7 class BailoutInfo { | 7 class BailoutInfo { |
| 8 int instructionId; | 8 int instructionId; |
| 9 int bailoutId; | 9 int bailoutId; |
| 10 BailoutInfo(this.instructionId, this.bailoutId); | 10 BailoutInfo(this.instructionId, this.bailoutId); |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 60 * Visits the graph in dominator order and inserts TypeGuards in places where | 60 * Visits the graph in dominator order and inserts TypeGuards in places where |
| 61 * we consider the guard to be of value. This phase also does type | 61 * we consider the guard to be of value. This phase also does type |
| 62 * propagation to help find valuable type guards. | 62 * propagation to help find valuable type guards. |
| 63 */ | 63 */ |
| 64 class SsaTypeGuardInserter extends SsaNonSpeculativeTypePropagator | 64 class SsaTypeGuardInserter extends SsaNonSpeculativeTypePropagator |
| 65 implements OptimizationPhase { | 65 implements OptimizationPhase { |
| 66 final String name = 'SsaTypeGuardInserter'; | 66 final String name = 'SsaTypeGuardInserter'; |
| 67 final CodegenWorkItem work; | 67 final CodegenWorkItem work; |
| 68 bool calledInLoop = false; | 68 bool calledInLoop = false; |
| 69 bool isRecursiveMethod = false; | 69 bool isRecursiveMethod = false; |
| 70 bool hasInsertedChecks = false; |
| 70 int stateId = 1; | 71 int stateId = 1; |
| 71 Map<HInstruction, HType> savedTypes = new Map<HInstruction, HType>(); | 72 Map<HInstruction, HType> savedTypes = new Map<HInstruction, HType>(); |
| 72 | 73 |
| 73 SsaTypeGuardInserter(compiler, this.work) : super(compiler); | 74 SsaTypeGuardInserter(compiler, this.work) : super(compiler); |
| 74 | 75 |
| 75 void visitGraph(HGraph graph) { | 76 void visitGraph(HGraph graph) { |
| 76 // Run the speculative type propagator. This does in-place | 77 // Run the speculative type propagator. This does in-place |
| 77 // update of the type of the instructions, and saves the | 78 // update of the type of the instructions, and saves the |
| 78 // previous types in the [savedTypes] map. | 79 // previous types in the [savedTypes] map. |
| 79 SsaTypePropagator propagator = | 80 SsaTypePropagator propagator = |
| (...skipping 26 matching lines...) Expand all Loading... |
| 106 } | 107 } |
| 107 | 108 |
| 108 // Primitive types that are not null are valuable. These include | 109 // Primitive types that are not null are valuable. These include |
| 109 // indexable arrays. | 110 // indexable arrays. |
| 110 bool typeValuable(HType type) { | 111 bool typeValuable(HType type) { |
| 111 return type.isPrimitive() && !type.isNull(); | 112 return type.isPrimitive() && !type.isNull(); |
| 112 } | 113 } |
| 113 | 114 |
| 114 bool get hasTypeGuards => work.guards.length != 0; | 115 bool get hasTypeGuards => work.guards.length != 0; |
| 115 | 116 |
| 117 bool isUsedWithIncompatibleSelector(HInstruction instruction, |
| 118 HType speculativeType) { |
| 119 for (HInstruction user in instruction.usedBy) { |
| 120 if (user is HCheck |
| 121 && isUsedWithIncompatibleSelector(user, speculativeType)) { |
| 122 return true; |
| 123 } else if (user.selector != null |
| 124 && user.getDartReceiver(compiler) == instruction |
| 125 && !speculativeType.computeMask(compiler).willHit( |
| 126 user.selector, compiler)) { |
| 127 print('$speculativeType and ${user.selector} disagree'); |
| 128 return true; |
| 129 } |
| 130 } |
| 131 return false; |
| 132 } |
| 133 |
| 116 bool typeGuardWouldBeValuable(HInstruction instruction, | 134 bool typeGuardWouldBeValuable(HInstruction instruction, |
| 117 HType speculativeType) { | 135 HType speculativeType) { |
| 118 // If the type itself is not valuable, do not generate a guard for it. | 136 // If the type itself is not valuable, do not generate a guard for it. |
| 119 if (!typeValuable(speculativeType)) return false; | 137 if (!typeValuable(speculativeType)) return false; |
| 120 | 138 |
| 121 // Do not insert a type guard if the instruction has a type | 139 // Do not insert a type guard if the instruction has a type |
| 122 // annotation that disagrees with the speculated type. | 140 // annotation that disagrees with the speculated type. |
| 123 Element source = instruction.sourceElement; | 141 Element source = instruction.sourceElement; |
| 124 if (source != null) { | 142 if (source != null) { |
| 125 DartType sourceType = source.computeType(compiler); | 143 DartType sourceType = source.computeType(compiler); |
| 126 if (!sourceType.isMalformed && !sourceType.isDynamic && | 144 if (!sourceType.isMalformed && !sourceType.isDynamic && |
| 127 sourceType.kind == TypeKind.INTERFACE) { | 145 sourceType.kind == TypeKind.INTERFACE) { |
| 128 TypeMask sourceMask = new TypeMask.subtype(sourceType); | 146 TypeMask sourceMask = new TypeMask.subtype(sourceType); |
| 129 TypeMask speculatedMask = speculativeType.computeMask(compiler); | 147 TypeMask speculatedMask = speculativeType.computeMask(compiler); |
| 130 if (sourceMask.intersection(speculatedMask, compiler).isEmpty) { | 148 if (sourceMask.intersection(speculatedMask, compiler).isEmpty) { |
| 131 return false; | 149 return false; |
| 132 } | 150 } |
| 133 } | 151 } |
| 134 } | 152 } |
| 135 | 153 |
| 154 // Do not insert a type guard if one of the calls on it will hit |
| 155 // [NoSuchMethodError]. |
| 156 if (isUsedWithIncompatibleSelector(instruction, speculativeType)) { |
| 157 return false; |
| 158 } |
| 159 |
| 136 // Insert type guards for recursive methods. | 160 // Insert type guards for recursive methods. |
| 137 if (isRecursiveMethod) return true; | 161 if (isRecursiveMethod) return true; |
| 138 | 162 |
| 139 // Insert type guards if there are uses in loops. | 163 // Insert type guards if there are uses in loops. |
| 140 bool isNested(HBasicBlock inner, HBasicBlock outer) { | 164 bool isNested(HBasicBlock inner, HBasicBlock outer) { |
| 141 if (identical(inner, outer)) return false; | 165 if (identical(inner, outer)) return false; |
| 142 if (outer == null) return true; | 166 if (outer == null) return true; |
| 143 while (inner != null) { | 167 while (inner != null) { |
| 144 if (identical(inner, outer)) return true; | 168 if (identical(inner, outer)) return true; |
| 145 inner = inner.parentLoopHeader; | 169 inner = inner.parentLoopHeader; |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 183 && typeGuardWouldBeValuable(user, speculativeType)) { | 207 && typeGuardWouldBeValuable(user, speculativeType)) { |
| 184 return true; | 208 return true; |
| 185 } | 209 } |
| 186 } | 210 } |
| 187 | 211 |
| 188 // Insert type guards if the method is likely to be called in a | 212 // Insert type guards if the method is likely to be called in a |
| 189 // loop. | 213 // loop. |
| 190 return calledInLoop; | 214 return calledInLoop; |
| 191 } | 215 } |
| 192 | 216 |
| 217 // Returns whether an invocation of [selector] on [receiver] will throw a |
| 218 // [ArgumentError] if the argument is not of the right type. |
| 219 bool willThrowArgumentError(Selector selector, HInstruction receiver) { |
| 220 if (receiver != null && (receiver.isInteger() || receiver.isString())) { |
| 221 return selector.isOperator() && selector.name != const SourceString('=='); |
| 222 } |
| 223 return false; |
| 224 } |
| 225 |
| 226 // Returns whether an invocation of [selector] will throw a |
| 227 // [NoSuchMethodError] if the receiver is not of the type |
| 228 // [speculativeType]. |
| 229 bool willThrowNoSuchMethodErrorIfNot(Selector selector, |
| 230 HType speculativeType) { |
| 231 return compiler.world.hasSingleMatch(selector) |
| 232 // In some cases, we want the receiver to be an integer, |
| 233 // but that does not mean we will get a NoSuchMethodError |
| 234 // if it's not: the receiver could be a double. |
| 235 && !speculativeType.isInteger() |
| 236 // We speculate on the [operator==] instruction, but we know it |
| 237 // will never throw a [NoSuchMethodError]. |
| 238 && selector.name != const SourceString('=='); |
| 239 } |
| 240 |
| 193 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) { | 241 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) { |
| 194 if (!speculativeType.isUseful()) return false; | 242 if (!speculativeType.isUseful()) return false; |
| 195 // If the types agree we don't need to check. | 243 // If the types agree we don't need to check. |
| 196 if (speculativeType == instruction.instructionType) return false; | 244 if (speculativeType == instruction.instructionType) return false; |
| 197 // If a bailout check is more expensive than doing the actual operation | 245 // If a bailout check is more expensive than doing the actual operation |
| 198 // don't do it either. | 246 // don't do it either. |
| 199 return typeGuardWouldBeValuable(instruction, speculativeType); | 247 return typeGuardWouldBeValuable(instruction, speculativeType); |
| 200 } | 248 } |
| 201 | 249 |
| 250 HInstruction computeFirstDominatingUserWithSelector( |
| 251 HInstruction instruction) { |
| 252 // TODO(ngeoffray): We currently only look at the instruction's |
| 253 // block, so that we know it will be executed. We should lift this |
| 254 // limitation. |
| 255 |
| 256 // For a parameter, we look at the first block that contains |
| 257 // user instructions. |
| 258 HBasicBlock userMustBeInBlock = instruction is HParameterValue |
| 259 ? instruction.block.successors[0] |
| 260 : instruction.block; |
| 261 |
| 262 HInstruction firstUser; |
| 263 for (HInstruction user in instruction.usedBy) { |
| 264 if (user.block == userMustBeInBlock && user.selector != null) { |
| 265 if (firstUser == null || user.dominates(firstUser)) { |
| 266 firstUser = user; |
| 267 } |
| 268 } |
| 269 } |
| 270 return firstUser; |
| 271 } |
| 272 |
| 273 /** |
| 274 * Tries to insert a type conversion instruction for [instruction] |
| 275 * instead of a type guard if we know an user will throw. Returns |
| 276 * whether it succeeded at adding a type conversion instruction. |
| 277 */ |
| 278 bool tryTypeConversion(HInstruction instruction, HType speculativeType) { |
| 279 HInstruction firstUser = |
| 280 computeFirstDominatingUserWithSelector(instruction); |
| 281 if (firstUser == null) return false; |
| 282 |
| 283 // If we have found a user with a selector, we find out if it |
| 284 // will throw [NoSuchMethodError] or [ArgumentError]. |
| 285 Selector selector = firstUser.selector; |
| 286 Selector receiverSelectorOnThrow = null; |
| 287 HInstruction receiver = firstUser.getDartReceiver(compiler); |
| 288 bool willThrow = false; |
| 289 if (receiver == instruction) { |
| 290 if (willThrowNoSuchMethodErrorIfNot(selector, speculativeType)) { |
| 291 receiverSelectorOnThrow = selector; |
| 292 willThrow = true; |
| 293 } |
| 294 } else if (willThrowArgumentError(selector, receiver)) { |
| 295 willThrow = true; |
| 296 } |
| 297 |
| 298 if (!willThrow) return false; |
| 299 |
| 300 HTypeConversion check = new HTypeConversion( |
| 301 null, |
| 302 receiverSelectorOnThrow == null |
| 303 ? HTypeConversion.ARGUMENT_TYPE_CHECK |
| 304 : HTypeConversion.RECEIVER_TYPE_CHECK, |
| 305 speculativeType, |
| 306 instruction, |
| 307 receiverSelectorOnThrow); |
| 308 hasInsertedChecks = true; |
| 309 firstUser.block.addBefore(firstUser, check); |
| 310 instruction.replaceAllUsersDominatedBy(firstUser, check); |
| 311 return true; |
| 312 } |
| 313 |
| 202 bool updateType(HInstruction instruction) { | 314 bool updateType(HInstruction instruction) { |
| 203 bool hasChanged = super.updateType(instruction); | 315 bool hasChanged = super.updateType(instruction); |
| 204 HType speculativeType = savedTypes[instruction]; | 316 HType speculativeType = savedTypes[instruction]; |
| 205 if (speculativeType == null) return hasChanged; | 317 if (speculativeType == null) return hasChanged; |
| 206 | 318 |
| 207 if (shouldInsertTypeGuard(instruction, speculativeType)) { | 319 if (shouldInsertTypeGuard(instruction, speculativeType) |
| 320 && !tryTypeConversion(instruction, speculativeType)) { |
| 208 HInstruction insertionPoint; | 321 HInstruction insertionPoint; |
| 209 if (instruction is HPhi) { | 322 if (instruction is HPhi) { |
| 210 insertionPoint = instruction.block.first; | 323 insertionPoint = instruction.block.first; |
| 211 } else if (instruction is HParameterValue) { | 324 } else if (instruction is HParameterValue) { |
| 212 // We insert the type guard at the end of the entry block | 325 // We insert the type guard at the end of the entry block |
| 213 // because if a parameter is live, it must be kept in the live | 326 // because if a parameter is live, it must be kept in the live |
| 214 // environment. Not doing so would mean we could visit a | 327 // environment. Not doing so would mean we could visit a |
| 215 // parameter and remove it from the environment before | 328 // parameter and remove it from the environment before |
| 216 // visiting a type guard. | 329 // visiting a type guard. |
| 217 insertionPoint = instruction.block.last; | 330 insertionPoint = instruction.block.last; |
| (...skipping 402 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 620 hasComplexBailoutTargets = true; | 733 hasComplexBailoutTargets = true; |
| 621 } | 734 } |
| 622 } else { | 735 } else { |
| 623 hasComplexBailoutTargets = true; | 736 hasComplexBailoutTargets = true; |
| 624 blocks.forEach((HBasicBlock block) { | 737 blocks.forEach((HBasicBlock block) { |
| 625 block.bailoutTargets.add(target); | 738 block.bailoutTargets.add(target); |
| 626 }); | 739 }); |
| 627 } | 740 } |
| 628 } | 741 } |
| 629 } | 742 } |
| OLD | NEW |