Chromium Code Reviews| 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 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 183 && typeGuardWouldBeValuable(user, speculativeType)) { | 184 && typeGuardWouldBeValuable(user, speculativeType)) { |
| 184 return true; | 185 return true; |
| 185 } | 186 } |
| 186 } | 187 } |
| 187 | 188 |
| 188 // Insert type guards if the method is likely to be called in a | 189 // Insert type guards if the method is likely to be called in a |
| 189 // loop. | 190 // loop. |
| 190 return calledInLoop; | 191 return calledInLoop; |
| 191 } | 192 } |
| 192 | 193 |
| 194 // Returns whether an invcation of [selector] on [receiver] will throw a | |
|
kasperl
2013/04/23 06:12:43
invcation -> invocation
ngeoffray
2013/04/23 08:06:04
Done.
| |
| 195 // [ArgumentError] if the argument is not of the right type. | |
| 196 bool willThrowArgumentError(Selector selector, HInstruction receiver) { | |
| 197 if (receiver != null && (receiver.isInteger() || receiver.isString())) { | |
| 198 return selector.isOperator() && selector.name != const SourceString('=='); | |
| 199 } | |
| 200 return false; | |
| 201 } | |
| 202 | |
| 203 // Returns whether an invocation of [selector] will throw a | |
| 204 // [NoSuchMethodError] if the receiver is not of the type | |
| 205 // [speculativeType]. | |
| 206 bool willThrowNoSuchMethodErrorIfNot(Selector selector, | |
| 207 HType speculativeType) { | |
| 208 return compiler.world.hasSingleMatch(selector) | |
| 209 // In some cases, we want the receiver to be an integer, | |
| 210 // but that does not mean we will get a NoSuchMethodError | |
| 211 // if it's not: the receiver could be a double. | |
| 212 && !speculativeType.isInteger() | |
| 213 // We speculate on the [operator==] instruction, but we know it | |
| 214 // will never throw a [NoSuchMethodError]. | |
| 215 && selector.name != const SourceString('=='); | |
| 216 } | |
| 217 | |
| 193 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) { | 218 bool shouldInsertTypeGuard(HInstruction instruction, HType speculativeType) { |
| 194 if (!speculativeType.isUseful()) return false; | 219 if (!speculativeType.isUseful()) return false; |
| 195 // If the types agree we don't need to check. | 220 // If the types agree we don't need to check. |
| 196 if (speculativeType == instruction.instructionType) return false; | 221 if (speculativeType == instruction.instructionType) return false; |
| 197 // If a bailout check is more expensive than doing the actual operation | 222 // If a bailout check is more expensive than doing the actual operation |
| 198 // don't do it either. | 223 // don't do it either. |
| 199 return typeGuardWouldBeValuable(instruction, speculativeType); | 224 return typeGuardWouldBeValuable(instruction, speculativeType); |
| 200 } | 225 } |
| 201 | 226 |
| 202 bool updateType(HInstruction instruction) { | 227 bool updateType(HInstruction instruction) { |
| 203 bool hasChanged = super.updateType(instruction); | 228 bool hasChanged = super.updateType(instruction); |
| 204 HType speculativeType = savedTypes[instruction]; | 229 HType speculativeType = savedTypes[instruction]; |
| 205 if (speculativeType == null) return hasChanged; | 230 if (speculativeType == null) return hasChanged; |
| 206 | 231 |
| 207 if (shouldInsertTypeGuard(instruction, speculativeType)) { | 232 if (shouldInsertTypeGuard(instruction, speculativeType)) { |
| 208 HInstruction insertionPoint; | 233 HInstruction insertionPoint; |
| 209 if (instruction is HPhi) { | 234 if (instruction is HPhi) { |
| 210 insertionPoint = instruction.block.first; | 235 insertionPoint = instruction.block.first; |
| 211 } else if (instruction is HParameterValue) { | 236 } else if (instruction is HParameterValue) { |
| 212 // We insert the type guard at the end of the entry block | 237 // 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 | 238 // because if a parameter is live, it must be kept in the live |
| 214 // environment. Not doing so would mean we could visit a | 239 // environment. Not doing so would mean we could visit a |
| 215 // parameter and remove it from the environment before | 240 // parameter and remove it from the environment before |
| 216 // visiting a type guard. | 241 // visiting a type guard. |
| 217 insertionPoint = instruction.block.last; | 242 insertionPoint = instruction.block.last; |
| 218 } else { | 243 } else { |
| 219 insertionPoint = instruction.next; | 244 insertionPoint = instruction.next; |
| 220 } | 245 } |
| 221 // If the previous instruction is also a type guard, then both | 246 |
| 222 // guards have the same environment, and can therefore share the | 247 // Find out if we should actually just emit a check for it. If |
|
kasperl
2013/04/23 06:12:43
It would be nice if this could be refactored into
ngeoffray
2013/04/23 08:06:04
Done.
| |
| 223 // same state id. | 248 // there is a user of [instruction] in the same block (so that |
| 224 HBailoutTarget target; | 249 // we know it will be executed), and that user has a selector |
| 225 int state; | 250 // (meaning it will be a call), we can put a type check instead |
| 226 if (insertionPoint.previous is HTypeGuard) { | 251 // of a type guard, that will either throw a [NoSuchMethodError] |
| 227 HTypeGuard other = insertionPoint.previous; | 252 // or a [ArgumentError]. |
| 228 target = other.bailoutTarget; | 253 bool willThrow = false; |
| 254 Selector receiverSelectorOnThrow = null; | |
| 255 HInstruction firstUserWithSelector; | |
| 256 HInstruction firstUser; | |
| 257 // For a parameter, we look at the first block that contains | |
| 258 // user instructions. | |
| 259 HBasicBlock userMustBeInBlock = instruction is HParameterValue | |
| 260 ? instruction.block.successors[0] | |
| 261 : instruction.block; | |
| 262 | |
| 263 // We distinguish between [firstUser] and | |
| 264 // [firstUserWithSelector] because we can often have the pattern | |
| 265 // that [firstUser] is a call to [: getInterceptor :] and | |
| 266 // [firstUserWithSelector] is the actual user of the | |
| 267 // instruction. | |
| 268 for (HInstruction user in instruction.usedBy) { | |
|
kasperl
2013/04/23 06:12:43
Somehow it feels like we could use a better strate
ngeoffray
2013/04/23 08:06:04
Agree. After discussing a bit about it, I'll work
| |
| 269 if (user.block == userMustBeInBlock) { | |
| 270 if (firstUser == null || user.dominates(firstUser)) { | |
| 271 firstUser = user; | |
| 272 if (user.selector != null) { | |
| 273 firstUserWithSelector = user; | |
| 274 } | |
| 275 } else if (user.selector != null) { | |
| 276 if (firstUserWithSelector == null | |
| 277 || user.dominates(firstUserWithSelector)) { | |
| 278 firstUserWithSelector = user; | |
| 279 } | |
| 280 } | |
| 281 } | |
| 282 } | |
| 283 | |
| 284 // If we have found a user with a selector, we find out if it | |
| 285 // will throw [NoSuchMethodError] or [ArgumentError]. | |
| 286 if (firstUserWithSelector != null | |
| 287 && (firstUserWithSelector == firstUser | |
| 288 || firstUser.next == firstUserWithSelector)) { | |
| 289 assert(firstUser == firstUserWithSelector || !firstUser.hasSideEffects() ); | |
|
kasperl
2013/04/23 06:12:43
Long line.
ngeoffray
2013/04/23 08:06:04
Done.
| |
| 290 Selector selector = firstUserWithSelector.selector; | |
| 291 HInstruction receiver = firstUserWithSelector.getDartReceiver(compiler); | |
| 292 if (receiver == instruction) { | |
| 293 if (willThrowNoSuchMethodErrorIfNot(selector, speculativeType)) { | |
| 294 receiverSelectorOnThrow = firstUserWithSelector.selector; | |
| 295 willThrow = true; | |
| 296 } | |
| 297 } else if (willThrowArgumentError(selector, receiver)) { | |
| 298 willThrow = true; | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 var check; | |
| 303 if (willThrow) { | |
| 304 insertionPoint = firstUser; | |
| 305 check = new HTypeConversion( | |
| 306 null, | |
| 307 receiverSelectorOnThrow == null | |
| 308 ? HTypeConversion.ARGUMENT_TYPE_CHECK | |
| 309 : HTypeConversion.RECEIVER_TYPE_CHECK, | |
| 310 speculativeType, | |
| 311 instruction, | |
| 312 receiverSelectorOnThrow); | |
| 313 hasInsertedChecks = true; | |
| 229 } else { | 314 } else { |
| 230 state = stateId++; | 315 // If the previous instruction is also a type guard, then both |
| 231 target = new HBailoutTarget(state); | 316 // guards have the same environment, and can therefore share the |
| 232 insertionPoint.block.addBefore(insertionPoint, target); | 317 // same state id. |
| 318 HBailoutTarget target; | |
| 319 int state; | |
| 320 if (insertionPoint.previous is HTypeGuard) { | |
| 321 HTypeGuard other = insertionPoint.previous; | |
| 322 target = other.bailoutTarget; | |
| 323 } else { | |
| 324 state = stateId++; | |
| 325 target = new HBailoutTarget(state); | |
| 326 insertionPoint.block.addBefore(insertionPoint, target); | |
| 327 } | |
| 328 check = new HTypeGuard(speculativeType, instruction, target); | |
| 329 work.guards.add(check); | |
| 330 // By setting the type of the guard to the speculated type, we | |
| 331 // help the analysis find valuable type guards. This however | |
| 332 // requires to run a non-speculative type propagation again | |
| 333 // after this analysis. | |
| 334 check.instructionType = speculativeType; | |
| 233 } | 335 } |
| 234 HTypeGuard guard = new HTypeGuard(speculativeType, instruction, target); | 336 instruction.block.rewrite(instruction, check); |
| 235 work.guards.add(guard); | 337 insertionPoint.block.addBefore(insertionPoint, check); |
| 236 // By setting the type of the guard to the speculated type, we | |
| 237 // help the analysis find valuable type guards. This however | |
| 238 // requires to run a non-speculative type propagation again | |
| 239 // after this analysis. | |
| 240 guard.instructionType = speculativeType; | |
| 241 instruction.block.rewrite(instruction, guard); | |
| 242 insertionPoint.block.addBefore(insertionPoint, guard); | |
| 243 } | 338 } |
| 244 return hasChanged; | 339 return hasChanged; |
| 245 } | 340 } |
| 246 } | 341 } |
| 247 | 342 |
| 248 /** | 343 /** |
| 249 * Computes the environment for each SSA instruction: visits the graph | 344 * Computes the environment for each SSA instruction: visits the graph |
| 250 * in post-dominator order. Removes an instruction from the environment | 345 * in post-dominator order. Removes an instruction from the environment |
| 251 * and adds its inputs to the environment at the instruction's | 346 * and adds its inputs to the environment at the instruction's |
| 252 * definition. | 347 * definition. |
| (...skipping 367 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 620 hasComplexBailoutTargets = true; | 715 hasComplexBailoutTargets = true; |
| 621 } | 716 } |
| 622 } else { | 717 } else { |
| 623 hasComplexBailoutTargets = true; | 718 hasComplexBailoutTargets = true; |
| 624 blocks.forEach((HBasicBlock block) { | 719 blocks.forEach((HBasicBlock block) { |
| 625 block.bailoutTargets.add(target); | 720 block.bailoutTargets.add(target); |
| 626 }); | 721 }); |
| 627 } | 722 } |
| 628 } | 723 } |
| 629 } | 724 } |
| OLD | NEW |