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 class BailoutInfo { | 5 class BailoutInfo { |
| 6 int instructionId; | 6 int instructionId; |
| 7 int bailoutId; | 7 int bailoutId; |
| 8 BailoutInfo(this.instructionId, this.bailoutId); | 8 BailoutInfo(this.instructionId, this.bailoutId); |
| 9 } | 9 } |
| 10 | 10 |
| (...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 145 if (isNested(userLoopHeader, currentLoopHeader)) return true; | 145 if (isNested(userLoopHeader, currentLoopHeader)) return true; |
| 146 } | 146 } |
| 147 | 147 |
| 148 // To speed up computations on values loaded from arrays, we | 148 // To speed up computations on values loaded from arrays, we |
| 149 // insert type guards for builtin array indexing operations in | 149 // insert type guards for builtin array indexing operations in |
| 150 // nested loops. Since this can blow up code size quite | 150 // nested loops. Since this can blow up code size quite |
| 151 // significantly, we only do it if type guards have already been | 151 // significantly, we only do it if type guards have already been |
| 152 // inserted for this method. The code size price for an additional | 152 // inserted for this method. The code size price for an additional |
| 153 // type guard is much smaller than the first one that causes the | 153 // type guard is much smaller than the first one that causes the |
| 154 // generation of a bailout method. | 154 // generation of a bailout method. |
| 155 if (instruction is HIndex && instruction.builtin && hasTypeGuards) { | 155 if (instruction is HIndex && |
| 156 (instruction as HIndex).builtin && | |
| 157 hasTypeGuards) { | |
| 156 HBasicBlock loopHeader = instruction.block.enclosingLoopHeader; | 158 HBasicBlock loopHeader = instruction.block.enclosingLoopHeader; |
| 157 if (loopHeader != null && loopHeader.parentLoopHeader != null) { | 159 if (loopHeader != null && loopHeader.parentLoopHeader != null) { |
| 158 return true; | 160 return true; |
| 159 } | 161 } |
| 160 } | 162 } |
| 161 | 163 |
| 162 // Insert type guards if the method is likely to be called in a | 164 // Insert type guards if the method is likely to be called in a |
| 163 // loop. | 165 // loop. |
| 164 return calledInLoop; | 166 return calledInLoop; |
| 165 } | 167 } |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 182 // If the types agree we don't need to check. | 184 // If the types agree we don't need to check. |
| 183 if (speculativeType == computedType) return false; | 185 if (speculativeType == computedType) return false; |
| 184 // If a bailout check is more expensive than doing the actual operation | 186 // If a bailout check is more expensive than doing the actual operation |
| 185 // don't do it either. | 187 // don't do it either. |
| 186 return typeGuardWouldBeValuable(instruction, speculativeType); | 188 return typeGuardWouldBeValuable(instruction, speculativeType); |
| 187 } | 189 } |
| 188 | 190 |
| 189 void visitInstruction(HInstruction instruction) { | 191 void visitInstruction(HInstruction instruction) { |
| 190 HType speculativeType = instruction.propagatedType; | 192 HType speculativeType = instruction.propagatedType; |
| 191 if (shouldInsertTypeGuard(instruction)) { | 193 if (shouldInsertTypeGuard(instruction)) { |
| 192 List<HInstruction> inputs = <HInstruction>[instruction]; | |
| 193 HInstruction insertionPoint; | 194 HInstruction insertionPoint; |
| 194 if (instruction is HPhi) { | 195 if (instruction is HPhi) { |
| 195 insertionPoint = instruction.block.first; | 196 insertionPoint = instruction.block.first; |
| 196 } else if (instruction is HParameterValue) { | 197 } else if (instruction is HParameterValue) { |
| 197 // We insert the type guard at the end of the entry block | 198 // We insert the type guard at the end of the entry block |
| 198 // because if a parameter is live, it must be kept in the live | 199 // because if a parameter is live, it must be kept in the live |
| 199 // environment. Not doing so would mean we could visit a | 200 // environment. Not doing so would mean we could visit a |
| 200 // parameter and remove it from the environment before | 201 // parameter and remove it from the environment before |
| 201 // visiting a type guard. | 202 // visiting a type guard. |
| 202 insertionPoint = instruction.block.last; | 203 insertionPoint = instruction.block.last; |
| 203 } else { | 204 } else { |
| 204 insertionPoint = instruction.next; | 205 insertionPoint = instruction.next; |
| 205 } | 206 } |
| 206 // If the previous instruction is also a type guard, then both | 207 // If the previous instruction is also a type guard, then both |
| 207 // guards have the same environment, and can therefore share the | 208 // guards have the same environment, and can therefore share the |
| 208 // same state id. | 209 // same state id. |
| 210 HBailoutTarget target; | |
| 209 int state; | 211 int state; |
| 210 if (insertionPoint.previous is HTypeGuard) { | 212 if (insertionPoint.previous is HTypeGuard) { |
| 211 HTypeGuard other = insertionPoint.previous; | 213 HTypeGuard other = insertionPoint.previous; |
| 212 state = other.state; | 214 target = other.bailoutTarget; |
| 213 } else { | 215 } else { |
| 214 state = stateId++; | 216 state = stateId++; |
| 217 target = new HBailoutTarget(state); | |
| 218 insertionPoint.block.addBefore(insertionPoint, target); | |
| 215 } | 219 } |
| 216 HTypeGuard guard = new HTypeGuard(speculativeType, state, inputs); | 220 HTypeGuard guard = new HTypeGuard(speculativeType, instruction, target); |
| 217 guard.propagatedType = speculativeType; | 221 guard.propagatedType = speculativeType; |
| 218 work.guards.add(guard); | 222 work.guards.add(guard); |
| 219 instruction.block.rewrite(instruction, guard); | 223 instruction.block.rewrite(instruction, guard); |
| 220 insertionPoint.block.addBefore(insertionPoint, guard); | 224 insertionPoint.block.addBefore(insertionPoint, guard); |
| 221 } | 225 } |
| 222 } | 226 } |
| 223 } | 227 } |
| 224 | 228 |
| 225 /** | 229 /** |
| 226 * Computes the environment for each SSA instruction: visits the graph | 230 * Computes the environment for each SSA instruction: visits the graph |
| 227 * in post-dominator order. Removes an instruction from the environment | 231 * in post-dominator order. Removes an instruction from the environment |
| 228 * and adds its inputs to the environment at the instruction's | 232 * and adds its inputs to the environment at the instruction's |
| 229 * definition. | 233 * definition. |
| 230 * | 234 * |
| 231 * At the end of the computation, insert type guards in the graph. | 235 * At the end of the computation, insert type guards in the graph. |
| 232 */ | 236 */ |
| 233 class SsaEnvironmentBuilder extends HBaseVisitor implements OptimizationPhase { | 237 class SsaEnvironmentBuilder extends HBaseVisitor implements OptimizationPhase { |
| 234 final Compiler compiler; | 238 final Compiler compiler; |
| 235 final String name = 'SsaEnvironmentBuilder'; | 239 final String name = 'SsaEnvironmentBuilder'; |
| 236 | 240 |
| 237 final Map<HInstruction, Environment> capturedEnvironments; | 241 final Map<HBailoutTarget, Environment> capturedEnvironments; |
| 238 final Map<HBasicBlock, Environment> liveInstructions; | 242 final Map<HBasicBlock, Environment> liveInstructions; |
| 239 Environment environment; | 243 Environment environment; |
| 240 | 244 |
| 241 SsaEnvironmentBuilder(Compiler this.compiler) | 245 SsaEnvironmentBuilder(Compiler this.compiler) |
| 242 : capturedEnvironments = new Map<HInstruction, Environment>(), | 246 : capturedEnvironments = new Map<HBailoutTarget, Environment>(), |
| 243 liveInstructions = new Map<HBasicBlock, Environment>(); | 247 liveInstructions = new Map<HBasicBlock, Environment>(); |
| 244 | 248 |
| 245 | 249 |
| 246 void visitGraph(HGraph graph) { | 250 void visitGraph(HGraph graph) { |
| 247 visitPostDominatorTree(graph); | 251 visitPostDominatorTree(graph); |
| 248 if (!liveInstructions[graph.entry].isEmpty()) { | 252 if (!liveInstructions[graph.entry].isEmpty()) { |
| 249 compiler.internalError('Bailout environment computation', | 253 compiler.internalError('Bailout environment computation', |
| 250 node: compiler.currentElement.parseNode(compiler)); | 254 node: compiler.currentElement.parseNode(compiler)); |
| 251 } | 255 } |
| 252 updateLoopMarkers(); | 256 updateLoopMarkers(); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 316 // If the block is a loop header, we can remove the loop marker, | 320 // If the block is a loop header, we can remove the loop marker, |
| 317 // because it will just recompute the loop phis. | 321 // because it will just recompute the loop phis. |
| 318 if (block.isLoopHeader()) { | 322 if (block.isLoopHeader()) { |
| 319 environment.removeLoopMarker(block); | 323 environment.removeLoopMarker(block); |
| 320 } | 324 } |
| 321 | 325 |
| 322 // Finally save the liveInstructions of that block. | 326 // Finally save the liveInstructions of that block. |
| 323 liveInstructions[block] = environment; | 327 liveInstructions[block] = environment; |
| 324 } | 328 } |
| 325 | 329 |
| 326 void visitTypeGuard(HTypeGuard guard) { | 330 void visitBailoutTarget(HBailoutTarget target) { |
| 327 visitInstruction(guard); | 331 visitInstruction(target); |
| 328 capturedEnvironments[guard] = new Environment.from(environment); | 332 capturedEnvironments[target] = new Environment.from(environment); |
| 329 } | 333 } |
| 330 | 334 |
| 331 void visitInstruction(HInstruction instruction) { | 335 void visitInstruction(HInstruction instruction) { |
| 332 environment.remove(instruction); | 336 environment.remove(instruction); |
| 333 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | 337 for (int i = 0, len = instruction.inputs.length; i < len; i++) { |
| 334 environment.add(instruction.inputs[i]); | 338 environment.add(instruction.inputs[i]); |
| 335 } | 339 } |
| 336 } | 340 } |
| 337 | 341 |
| 342 /** | |
| 343 * Stores all live variables in the bailout target and the guards. | |
| 344 */ | |
| 338 void insertCapturedEnvironments() { | 345 void insertCapturedEnvironments() { |
| 339 Map<int, HTypeGuard> seenGuardStates = new Map<int, HTypeGuard>(); | 346 capturedEnvironments.forEach((HBailoutTarget target, Environment env) { |
| 340 capturedEnvironments.forEach((HTypeGuard guard, Environment env) { | 347 assert(target.inputs.length == 0); |
| 341 storeInGuard(guard, env.lives, seenGuardStates); | 348 target.inputs.addAll(env.lives); |
| 349 // TODO(floitsch): we should add the bailout-target's input variables | |
| 350 // as input to the guards only in the optimized version. The | |
| 351 // non-optimized version does not use the bailout guards and it is | |
| 352 // unnecessary to keep the variables alive until the check. | |
| 353 for (HTypeGuard guard in target.usedBy) { | |
| 354 assert(guard.inputs.length == 2); | |
|
ricow1
2012/07/23 12:52:26
Why is the length of the inputs always 2
floitsch
2012/07/23 13:27:48
Added comment:
// A type-guard initially only has
| |
| 355 guard.inputs.addAll(env.lives); | |
| 356 } | |
| 357 for (HInstruction live in env.lives) { | |
| 358 live.usedBy.add(target); | |
| 359 live.usedBy.addAll(target.usedBy); | |
| 360 } | |
| 342 }); | 361 }); |
| 343 } | 362 } |
| 344 | |
| 345 /** | |
| 346 * Stores all live variables in the guard. | |
| 347 */ | |
| 348 void storeInGuard(HTypeGuard guard, | |
| 349 Set<HInstruction> lives, | |
| 350 Map<int, HTypeGuard> seenGuardStates) { | |
| 351 HInstruction guarded = guard.guarded; | |
| 352 List<HInstruction> inputs = guard.inputs; | |
| 353 assert(inputs.length == 1); | |
| 354 inputs.clear(); | |
| 355 HTypeGuard other = seenGuardStates[guard.state]; | |
| 356 if (other !== null) { | |
| 357 // The guards are sharing the same state. Also share the same | |
| 358 // environment, in the same order. | |
| 359 inputs.addAll(other.inputs); | |
| 360 assert(inputs.length == lives.length); | |
| 361 } else { | |
| 362 seenGuardStates[guard.state] = guard; | |
| 363 inputs.addAll(lives); | |
| 364 } | |
| 365 | |
| 366 for (int i = 0; i < inputs.length; i++) { | |
| 367 HInstruction input = inputs[i]; | |
| 368 if (input == guarded) { | |
| 369 guard.checkedInputIndex = i; | |
| 370 // No need to update [input.usedBy], the guard is already | |
| 371 // there. | |
| 372 } else { | |
| 373 input.usedBy.add(guard); | |
| 374 } | |
| 375 } | |
| 376 } | |
| 377 } | 363 } |
| 378 | 364 |
| 379 /** | 365 /** |
| 380 * Propagates bailout information to blocks that need it. This visitor | 366 * Propagates bailout information to blocks that need it. This visitor |
| 381 * is run before codegen, to know which blocks have to deal with | 367 * is run before codegen, to know which blocks have to deal with |
| 382 * bailouts. | 368 * bailouts. |
| 383 */ | 369 */ |
| 384 class SsaBailoutPropagator extends HBaseVisitor { | 370 class SsaBailoutPropagator extends HBaseVisitor { |
| 385 final Compiler compiler; | 371 final Compiler compiler; |
| 386 final List<HBasicBlock> blocks; | 372 final List<HBasicBlock> blocks; |
| 387 final List<HLabeledBlockInformation> labeledBlockInformations; | 373 final List<HLabeledBlockInformation> labeledBlockInformations; |
| 388 final Set<HInstruction> generateAtUseSite; | 374 final Set<HInstruction> generateAtUseSite; |
| 389 SubGraph subGraph; | 375 SubGraph subGraph; |
| 390 int maxBailoutParameters = 0; | 376 int maxBailoutParameters = 0; |
| 391 | 377 |
| 392 /** | 378 /** |
| 393 * If set to true, the graph has either multiple bailouts in | 379 * If set to true, the graph has either multiple bailouts in |
| 394 * different places, or a bailout inside an if or a loop. For such a | 380 * different places, or a bailout inside an if or a loop. For such a |
| 395 * graph, the code generator will emit a generic switch. | 381 * graph, the code generator will emit a generic switch. |
| 396 */ | 382 */ |
| 397 bool hasComplexTypeGuards = false; | 383 bool hasComplexBailoutTargets = false; |
| 398 | 384 |
| 399 /** | 385 /** |
| 400 * The first type guard in the graph. | 386 * The first type guard in the graph. |
| 401 */ | 387 */ |
| 402 HTypeGuard firstTypeGuard; | 388 HBailoutTarget firstBailoutTarget; |
| 403 | 389 |
| 404 /** | 390 /** |
| 405 * If set, it is the first block in the graph where we generate | 391 * If set, it is the first block in the graph where we generate |
| 406 * code. Blocks before this one are dead code in the bailout | 392 * code. Blocks before this one are dead code in the bailout |
| 407 * version. | 393 * version. |
| 408 */ | 394 */ |
| 409 | 395 |
| 410 SsaBailoutPropagator(this.compiler, this.generateAtUseSite) | 396 SsaBailoutPropagator(this.compiler, this.generateAtUseSite) |
| 411 : blocks = <HBasicBlock>[], | 397 : blocks = <HBasicBlock>[], |
| 412 labeledBlockInformations = <HLabeledBlockInformation>[]; | 398 labeledBlockInformations = <HLabeledBlockInformation>[]; |
| (...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 515 | 501 |
| 516 visitBasicBlock(branchBlock.successors[1]); | 502 visitBasicBlock(branchBlock.successors[1]); |
| 517 // With labeled breaks we can have more dominated blocks. | 503 // With labeled breaks we can have more dominated blocks. |
| 518 if (dominated.length >= 3) { | 504 if (dominated.length >= 3) { |
| 519 for (int i = 2; i < dominated.length; i++) { | 505 for (int i = 2; i < dominated.length; i++) { |
| 520 visitBasicBlock(dominated[i]); | 506 visitBasicBlock(dominated[i]); |
| 521 } | 507 } |
| 522 } | 508 } |
| 523 } | 509 } |
| 524 | 510 |
| 525 visitTypeGuard(HTypeGuard guard) { | 511 visitBailoutTarget(HBailoutTarget target) { |
| 526 int inputLength = guard.inputs.length; | 512 int inputLength = target.inputs.length; |
| 527 if (inputLength > maxBailoutParameters) { | 513 if (inputLength > maxBailoutParameters) { |
| 528 maxBailoutParameters = inputLength; | 514 maxBailoutParameters = inputLength; |
| 529 } | 515 } |
| 530 if (blocks.isEmpty()) { | 516 if (blocks.isEmpty()) { |
| 531 if (firstTypeGuard === null || firstTypeGuard.state === guard.state) { | 517 if (firstBailoutTarget === null) { |
| 532 firstTypeGuard = guard; | 518 firstBailoutTarget = target; |
| 533 } else { | 519 } else { |
| 534 hasComplexTypeGuards = true; | 520 hasComplexBailoutTargets = true; |
| 535 } | 521 } |
| 536 } else { | 522 } else { |
| 537 hasComplexTypeGuards = true; | 523 hasComplexBailoutTargets = true; |
| 538 blocks.forEach((HBasicBlock block) { | 524 blocks.forEach((HBasicBlock block) { |
| 539 block.guards.add(guard); | 525 block.bailoutTargets.add(target); |
| 540 }); | 526 }); |
| 541 } | 527 } |
| 542 } | 528 } |
| 543 } | 529 } |
| OLD | NEW |