| 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 |
| 11 /** | 11 /** |
| 12 * Keeps track of the execution environment for instructions. An | 12 * Keeps track of the execution environment for instructions. An |
| 13 * execution environment contains the SSA instructions that are live. | 13 * execution environment contains the SSA instructions that are live. |
| 14 */ | 14 */ |
| 15 class Environment { | 15 class Environment { |
| 16 final Set<HInstruction> lives; | 16 final Set<HInstruction> lives; |
| 17 final Set<HBasicBlock> loopMarkers; | 17 final Set<HBasicBlock> loopMarkers; |
| 18 Environment() : lives = new Set<HInstruction>(), | 18 Environment() : lives = new Set<HInstruction>(), |
| 19 loopMarkers = new Set<HBasicBlock>(); | 19 loopMarkers = new Set<HBasicBlock>(); |
| 20 Environment.from(Environment other) | 20 Environment.from(Environment other) |
| 21 : lives = new Set<HInstruction>.from(other.lives), | 21 : lives = new Set<HInstruction>.from(other.lives), |
| 22 loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers); | 22 loopMarkers = new Set<HBasicBlock>.from(other.loopMarkers); |
| 23 | 23 |
| 24 void remove(HInstruction instruction) { | 24 void remove(HInstruction instruction) { |
| 25 lives.remove(instruction); | 25 lives.remove(instruction); |
| 26 } | 26 } |
| 27 | 27 |
| 28 void add(HInstruction instruction) { | 28 void add(HInstruction instruction) { |
| 29 // If the instruction is a type guard, we add its checked input | 29 if (!instruction.isCodeMotionInvariant()) { |
| 30 // instead. This allows sharing the same environment between | |
| 31 // different type guards. | |
| 32 // | |
| 33 // Also, we don't need to add code motion invariant instructions | |
| 34 // in the live set (because we generate them at use-site), except | |
| 35 // for parameters that are not 'this', which is always passed as | |
| 36 // the receiver. | |
| 37 if (instruction is HTypeGuard) { | |
| 38 add(instruction.checkedInput); | |
| 39 } else if (!instruction.isCodeMotionInvariant() | |
| 40 || (instruction is HParameterValue && instruction is !HThis)) { | |
| 41 lives.add(instruction); | 30 lives.add(instruction); |
| 42 } else { | 31 } else { |
| 43 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | 32 for (int i = 0, len = instruction.inputs.length; i < len; i++) { |
| 44 add(instruction.inputs[i]); | 33 add(instruction.inputs[i]); |
| 45 } | 34 } |
| 46 } | 35 } |
| 47 } | 36 } |
| 48 | 37 |
| 49 void addLoopMarker(HBasicBlock block) { | 38 void addLoopMarker(HBasicBlock block) { |
| 50 loopMarkers.add(block); | 39 loopMarkers.add(block); |
| 51 } | 40 } |
| 52 | 41 |
| 53 void removeLoopMarker(HBasicBlock block) { | 42 void removeLoopMarker(HBasicBlock block) { |
| 54 loopMarkers.remove(block); | 43 loopMarkers.remove(block); |
| 55 } | 44 } |
| 56 | 45 |
| 57 void addAll(Environment other) { | 46 void addAll(Environment other) { |
| 58 lives.addAll(other.lives); | 47 lives.addAll(other.lives); |
| 59 loopMarkers.addAll(other.loopMarkers); | 48 loopMarkers.addAll(other.loopMarkers); |
| 60 } | 49 } |
| 61 | 50 |
| 51 /** |
| 52 * Stores all live variables in the guard. The guarded instruction will be the |
| 53 * last input in the guard's input list. |
| 54 */ |
| 55 void storeInGuard(HTypeGuard guard) { |
| 56 HInstruction guarded = guard.guarded; |
| 57 List<HInstruction> inputs = guard.inputs; |
| 58 assert(inputs.length == 1); |
| 59 inputs.clear(); |
| 60 // Remove the guarded from the environment, so that we are sure it is last |
| 61 // when we add it again. |
| 62 remove(guarded); |
| 63 inputs.addAll(lives); |
| 64 inputs.addLast(guarded); |
| 65 add(guarded); |
| 66 for (int i = 0; i < inputs.length - 1; i++) { |
| 67 HInstruction input = inputs[i]; |
| 68 input.usedBy.add(guard); |
| 69 } |
| 70 } |
| 71 |
| 62 bool isEmpty() => lives.isEmpty() && loopMarkers.isEmpty(); | 72 bool isEmpty() => lives.isEmpty() && loopMarkers.isEmpty(); |
| 63 } | 73 } |
| 64 | 74 |
| 65 | 75 |
| 66 /** | 76 /** |
| 67 * Visits the graph in dominator order and inserts TypeGuards in places where | 77 * Visits the graph in dominator order and inserts TypeGuards in places where |
| 68 * we consider the guard to be of value. | 78 * we consider the guard to be of value. |
| 69 * | 79 * |
| 70 * Might modify the [:propagatedType:] fields of the instructions in an | 80 * Might modify the [:propagatedType:] fields of the instructions in an |
| 71 * inconsistent way. No further analysis should rely on them. | 81 * inconsistent way. No further analysis should rely on them. |
| (...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 167 if (speculativeType == computedType) return false; | 177 if (speculativeType == computedType) return false; |
| 168 // If a bailout check is more expensive than doing the actual operation | 178 // If a bailout check is more expensive than doing the actual operation |
| 169 // don't do it either. | 179 // don't do it either. |
| 170 return typeGuardWouldBeValuable(instruction, speculativeType); | 180 return typeGuardWouldBeValuable(instruction, speculativeType); |
| 171 } | 181 } |
| 172 | 182 |
| 173 void visitInstruction(HInstruction instruction) { | 183 void visitInstruction(HInstruction instruction) { |
| 174 HType speculativeType = instruction.propagatedType; | 184 HType speculativeType = instruction.propagatedType; |
| 175 if (shouldInsertTypeGuard(instruction)) { | 185 if (shouldInsertTypeGuard(instruction)) { |
| 176 List<HInstruction> inputs = <HInstruction>[instruction]; | 186 List<HInstruction> inputs = <HInstruction>[instruction]; |
| 177 HInstruction insertionPoint; | 187 HTypeGuard guard = new HTypeGuard(speculativeType, stateId++, inputs); |
| 178 if (instruction is HPhi) { | |
| 179 insertionPoint = instruction.block.first; | |
| 180 } else if (instruction is HParameterValue) { | |
| 181 // We insert the type guard at the end of the entry block | |
| 182 // because if a parameter is live, it must be kept in the live | |
| 183 // environment. Not doing so would mean we could visit a | |
| 184 // parameter and remove it from the environment before | |
| 185 // visiting a type guard. | |
| 186 insertionPoint = instruction.block.last; | |
| 187 } else { | |
| 188 insertionPoint = instruction.next; | |
| 189 } | |
| 190 // If the previous instruction is also a type guard, then both | |
| 191 // guards have the same environment, and can therefore share the | |
| 192 // same state id. | |
| 193 int state; | |
| 194 if (insertionPoint.previous is HTypeGuard) { | |
| 195 HTypeGuard other = insertionPoint.previous; | |
| 196 state = other.state; | |
| 197 } else { | |
| 198 state = stateId++; | |
| 199 } | |
| 200 HTypeGuard guard = new HTypeGuard(speculativeType, state, inputs); | |
| 201 guard.propagatedType = speculativeType; | 188 guard.propagatedType = speculativeType; |
| 202 work.guards.add(guard); | 189 work.guards.add(guard); |
| 203 instruction.block.rewrite(instruction, guard); | 190 instruction.block.rewrite(instruction, guard); |
| 191 HInstruction insertionPoint = (instruction is HPhi) |
| 192 ? instruction.block.first |
| 193 : instruction.next; |
| 204 insertionPoint.block.addBefore(insertionPoint, guard); | 194 insertionPoint.block.addBefore(insertionPoint, guard); |
| 205 } | 195 } |
| 206 } | 196 } |
| 207 } | 197 } |
| 208 | 198 |
| 209 /** | 199 /** |
| 210 * Computes the environment for each SSA instruction: visits the graph | 200 * Computes the environment for each SSA instruction: visits the graph |
| 211 * in post-dominator order. Removes an instruction from the environment | 201 * in post-dominator order. Removes an instruction from the environment |
| 212 * and adds its inputs to the environment at the instruction's | 202 * and adds its inputs to the environment at the instruction's |
| 213 * definition. | 203 * definition. |
| (...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 313 } | 303 } |
| 314 | 304 |
| 315 void visitInstruction(HInstruction instruction) { | 305 void visitInstruction(HInstruction instruction) { |
| 316 environment.remove(instruction); | 306 environment.remove(instruction); |
| 317 for (int i = 0, len = instruction.inputs.length; i < len; i++) { | 307 for (int i = 0, len = instruction.inputs.length; i < len; i++) { |
| 318 environment.add(instruction.inputs[i]); | 308 environment.add(instruction.inputs[i]); |
| 319 } | 309 } |
| 320 } | 310 } |
| 321 | 311 |
| 322 void insertCapturedEnvironments() { | 312 void insertCapturedEnvironments() { |
| 323 Map<int, HTypeGuard> seenGuardStates = new Map<int, HTypeGuard>(); | |
| 324 capturedEnvironments.forEach((HTypeGuard guard, Environment env) { | 313 capturedEnvironments.forEach((HTypeGuard guard, Environment env) { |
| 325 storeInGuard(guard, env.lives, seenGuardStates); | 314 env.storeInGuard(guard); |
| 326 }); | 315 }); |
| 327 } | 316 } |
| 328 | |
| 329 /** | |
| 330 * Stores all live variables in the guard. | |
| 331 */ | |
| 332 void storeInGuard(HTypeGuard guard, | |
| 333 Set<HInstruction> lives, | |
| 334 Map<int, HTypeGuard> seenGuardStates) { | |
| 335 HInstruction guarded = guard.guarded; | |
| 336 List<HInstruction> inputs = guard.inputs; | |
| 337 assert(inputs.length == 1); | |
| 338 inputs.clear(); | |
| 339 HTypeGuard other = seenGuardStates[guard.state]; | |
| 340 if (other !== null) { | |
| 341 // The guards are sharing the same state. Also share the same | |
| 342 // environment, in the same order. | |
| 343 inputs.addAll(other.inputs); | |
| 344 assert(inputs.length == lives.length); | |
| 345 } else { | |
| 346 seenGuardStates[guard.state] = guard; | |
| 347 inputs.addAll(lives); | |
| 348 } | |
| 349 | |
| 350 for (int i = 0; i < inputs.length; i++) { | |
| 351 HInstruction input = inputs[i]; | |
| 352 if (input == guarded) { | |
| 353 guard.checkedInputIndex = i; | |
| 354 // No need to update [input.usedBy], the guard is already | |
| 355 // there. | |
| 356 } else { | |
| 357 input.usedBy.add(guard); | |
| 358 } | |
| 359 } | |
| 360 } | |
| 361 } | 317 } |
| 362 | 318 |
| 363 /** | 319 /** |
| 364 * Propagates bailout information to blocks that need it. This visitor | 320 * Propagates bailout information to blocks that need it. This visitor |
| 365 * is run before codegen, to know which blocks have to deal with | 321 * is run before codegen, to know which blocks have to deal with |
| 366 * bailouts. | 322 * bailouts. |
| 367 */ | 323 */ |
| 368 class SsaBailoutPropagator extends HBaseVisitor { | 324 class SsaBailoutPropagator extends HBaseVisitor { |
| 369 final Compiler compiler; | 325 final Compiler compiler; |
| 370 final List<HBasicBlock> blocks; | 326 final List<HBasicBlock> blocks; |
| 371 final List<HLabeledBlockInformation> labeledBlockInformations; | 327 final List<HLabeledBlockInformation> labeledBlockInformations; |
| 372 final Set<HInstruction> generateAtUseSite; | |
| 373 SubGraph subGraph; | 328 SubGraph subGraph; |
| 374 | 329 |
| 375 /** | 330 SsaBailoutPropagator(Compiler this.compiler) |
| 376 * If set to true, the graph has either multiple bailouts in | |
| 377 * different places, or a bailout inside an if or a loop. For such a | |
| 378 * graph, the code generator will emit a generic switch. | |
| 379 */ | |
| 380 bool hasComplexTypeGuards = false; | |
| 381 | |
| 382 /** | |
| 383 * The first type guard in the graph. | |
| 384 */ | |
| 385 HTypeGuard firstTypeGuard; | |
| 386 | |
| 387 /** | |
| 388 * If set, it is the first block in the graph where we generate | |
| 389 * code. Blocks before this one are dead code in the bailout | |
| 390 * version. | |
| 391 */ | |
| 392 | |
| 393 SsaBailoutPropagator(this.compiler, this.generateAtUseSite) | |
| 394 : blocks = <HBasicBlock>[], | 331 : blocks = <HBasicBlock>[], |
| 395 labeledBlockInformations = <HLabeledBlockInformation>[]; | 332 labeledBlockInformations = <HLabeledBlockInformation>[]; |
| 396 | 333 |
| 397 void visitGraph(HGraph graph) { | 334 void visitGraph(HGraph graph) { |
| 398 subGraph = new SubGraph(graph.entry, graph.exit); | 335 subGraph = new SubGraph(graph.entry, graph.exit); |
| 336 blocks.addLast(graph.entry); |
| 399 visitBasicBlock(graph.entry); | 337 visitBasicBlock(graph.entry); |
| 338 blocks.removeLast(); |
| 400 if (!blocks.isEmpty()) { | 339 if (!blocks.isEmpty()) { |
| 401 compiler.internalError('Bailout propagation', | 340 compiler.internalError('Bailout propagation', |
| 402 node: compiler.currentElement.parseNode(compiler)); | 341 node: compiler.currentElement.parseNode(compiler)); |
| 403 } | 342 } |
| 404 } | 343 } |
| 405 | 344 |
| 406 void visitBasicBlock(HBasicBlock block) { | 345 void visitBasicBlock(HBasicBlock block) { |
| 407 // Abort traversal if we are leaving the currently active sub-graph. | 346 // Abort traversal if we are leaving the currently active sub-graph. |
| 408 if (!subGraph.contains(block)) return; | 347 if (!subGraph.contains(block)) return; |
| 409 | 348 |
| 410 if (block.isLoopHeader()) { | 349 if (block.isLoopHeader()) { |
| 411 blocks.addLast(block); | 350 blocks.addLast(block); |
| 412 } else if (block.isLabeledBlock() | 351 } else if (block.isLabeledBlock() && blocks.last() !== block) { |
| 413 && (blocks.isEmpty() || blocks.last() !== block)) { | |
| 414 HLabeledBlockInformation info = block.blockFlow.body; | 352 HLabeledBlockInformation info = block.blockFlow.body; |
| 415 visitStatements(info.body); | 353 visitStatements(info.body); |
| 416 return; | 354 return; |
| 417 } | 355 } |
| 418 | 356 |
| 419 HInstruction instruction = block.first; | 357 HInstruction instruction = block.first; |
| 420 while (instruction != null) { | 358 while (instruction != null) { |
| 421 instruction.accept(this); | 359 instruction.accept(this); |
| 422 instruction = instruction.next; | 360 instruction = instruction.next; |
| 423 } | 361 } |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 501 visitBasicBlock(branchBlock.successors[1]); | 439 visitBasicBlock(branchBlock.successors[1]); |
| 502 // With labeled breaks we can have more dominated blocks. | 440 // With labeled breaks we can have more dominated blocks. |
| 503 if (dominated.length >= 3) { | 441 if (dominated.length >= 3) { |
| 504 for (int i = 2; i < dominated.length; i++) { | 442 for (int i = 2; i < dominated.length; i++) { |
| 505 visitBasicBlock(dominated[i]); | 443 visitBasicBlock(dominated[i]); |
| 506 } | 444 } |
| 507 } | 445 } |
| 508 } | 446 } |
| 509 | 447 |
| 510 visitTypeGuard(HTypeGuard guard) { | 448 visitTypeGuard(HTypeGuard guard) { |
| 511 if (blocks.isEmpty()) { | 449 blocks.forEach((HBasicBlock block) { |
| 512 if (firstTypeGuard === null || firstTypeGuard.state === guard.state) { | 450 block.guards.add(guard); |
| 513 firstTypeGuard = guard; | 451 }); |
| 514 } else { | |
| 515 hasComplexTypeGuards = true; | |
| 516 } | |
| 517 } else { | |
| 518 hasComplexTypeGuards = true; | |
| 519 blocks.forEach((HBasicBlock block) { | |
| 520 block.guards.add(guard); | |
| 521 }); | |
| 522 } | |
| 523 } | 452 } |
| 524 } | 453 } |
| OLD | NEW |