Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 library tree_ir.optimization.variable_merger; | 5 library tree_ir.optimization.variable_merger; |
| 6 | 6 |
| 7 import 'optimization.dart' show Pass, PassMixin; | 7 import 'optimization.dart' show Pass, PassMixin; |
| 8 import '../tree_ir_nodes.dart'; | 8 import '../tree_ir_nodes.dart'; |
| 9 import '../../elements/elements.dart' show Local, ParameterElement; | 9 import '../../elements/elements.dart' show Local, ParameterElement; |
| 10 | 10 |
| 11 /// Merges variables based on liveness and source variable information. | 11 /// Merges variables based on liveness and source variable information. |
| 12 /// | 12 /// |
| 13 /// This phase cleans up artifacts introduced by the translation through CPS, | 13 /// This phase cleans up artifacts introduced by the translation through CPS, |
| 14 /// where each source variable is translated into several copies. The copies | 14 /// where each source variable is translated into several copies. The copies |
| 15 /// are merged again when they are not live simultaneously. | 15 /// are merged again when they are not live simultaneously. |
| 16 class VariableMerger extends RecursiveVisitor with PassMixin { | 16 class VariableMerger extends RecursiveVisitor implements Pass { |
| 17 String get passName => 'Variable merger'; | 17 String get passName => 'Variable merger'; |
| 18 | 18 |
| 19 void rewrite(RootNode node) { | |
| 20 rewriteFunction(node); | |
| 21 node.forEachBody(visitStatement); | |
| 22 } | |
| 23 | |
| 19 @override | 24 @override |
| 20 void rewriteExecutableDefinition(ExecutableDefinition node) { | 25 void visitInnerFunction(FunctionDefinition node) { |
| 21 visitExecutableDefinition(node); | 26 rewriteFunction(node); |
| 22 } | 27 } |
| 23 | 28 |
| 24 /// Rewrites the given function. | 29 /// Rewrites the given function. |
| 25 /// This is called for the outermost function and inner functions. | 30 /// This is called for the outermost function and inner functions. |
| 26 void rewriteFunction(ExecutableDefinition node) { | 31 void rewriteFunction(RootNode node) { |
| 27 BlockGraphBuilder builder = new BlockGraphBuilder(); | 32 node.forEachBody((Statement body) { |
| 28 builder.visitExecutableDefinition(node); | 33 BlockGraphBuilder builder = new BlockGraphBuilder(); |
| 29 _computeLiveness(builder.blocks); | 34 builder.build(node.parameters, body); |
| 30 Map<Variable, Variable> subst = _computeRegisterAllocation(builder.blocks); | 35 _computeLiveness(builder.blocks); |
| 31 new SubstituteVariables(subst).visitExecutableDefinition(node); | 36 Map<Variable, Variable> subst = |
| 32 } | 37 _computeRegisterAllocation(builder.blocks); |
| 33 | 38 new SubstituteVariables(subst).apply(node); |
| 34 visitFunctionDefinition(FunctionDefinition node) { | 39 }); |
| 35 super.visitFunctionDefinition(node); // Recurse to visit inner functions. | |
| 36 rewriteFunction(node); | |
| 37 } | |
| 38 | |
| 39 visitFieldDefinition(FieldDefinition node) { | |
| 40 super.visitFieldDefinition(node); | |
| 41 rewriteFunction(node); | |
| 42 } | |
| 43 | |
| 44 visitConstructorDefinition(ConstructorDefinition node) { | |
| 45 super.visitConstructorDefinition(node); | |
| 46 rewriteFunction(node); | |
| 47 } | 40 } |
| 48 } | 41 } |
| 49 | 42 |
| 50 /// A read or write access to a variable. | 43 /// A read or write access to a variable. |
| 51 class VariableAccess { | 44 class VariableAccess { |
| 52 Variable variable; | 45 Variable variable; |
| 53 bool isRead; | 46 bool isRead; |
| 54 bool get isWrite => !isRead; | 47 bool get isWrite => !isRead; |
| 55 | 48 |
| 56 VariableAccess.read(this.variable) : isRead = true; | 49 VariableAccess.read(this.variable) : isRead = true; |
| (...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 109 Map<Label, Block> _jumpTarget = <Label, Block>{}; | 102 Map<Label, Block> _jumpTarget = <Label, Block>{}; |
| 110 Block _currentBlock; | 103 Block _currentBlock; |
| 111 List<Block> blocks = <Block>[]; | 104 List<Block> blocks = <Block>[]; |
| 112 | 105 |
| 113 /// Variables with an assignment that should be treated as final. | 106 /// Variables with an assignment that should be treated as final. |
| 114 /// | 107 /// |
| 115 /// Such variables cannot be merged with any other variables, so we exclude | 108 /// Such variables cannot be merged with any other variables, so we exclude |
| 116 /// them from the control-flow graph entirely. | 109 /// them from the control-flow graph entirely. |
| 117 Set<Variable> _ignoredVariables = new Set<Variable>(); | 110 Set<Variable> _ignoredVariables = new Set<Variable>(); |
| 118 | 111 |
| 119 BlockGraphBuilder() { | 112 void build(List<Variable> parameters, Statement body) { |
| 120 _currentBlock = newBlock(); | 113 _currentBlock = newBlock(); |
| 114 parameters.forEach(write); | |
| 115 visitStatement(body); | |
| 116 } | |
| 117 | |
| 118 @override | |
| 119 void visitInnerFunction(FunctionDefinition node) { | |
| 120 // Do nothing. Inner functions are traversed in VariableMerger. | |
| 121 } | 121 } |
| 122 | 122 |
| 123 /// Creates a new block with the current exception handler or [catchBlock] | 123 /// Creates a new block with the current exception handler or [catchBlock] |
| 124 /// if provided. | 124 /// if provided. |
| 125 Block newBlock({Block catchBlock}) { | 125 Block newBlock({Block catchBlock}) { |
| 126 if (catchBlock == null && _currentBlock != null) { | 126 if (catchBlock == null && _currentBlock != null) { |
| 127 catchBlock = _currentBlock.catchBlock; | 127 catchBlock = _currentBlock.catchBlock; |
| 128 } | 128 } |
| 129 Block block = new Block(catchBlock); | 129 Block block = new Block(catchBlock); |
| 130 blocks.add(block); | 130 blocks.add(block); |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 237 visitExpression(node.left); | 237 visitExpression(node.left); |
| 238 // TODO(asgerf): When assignment expressions are added, this is no longer | 238 // TODO(asgerf): When assignment expressions are added, this is no longer |
| 239 // sound; then we need to handle as a branch. | 239 // sound; then we need to handle as a branch. |
| 240 visitExpression(node.right); | 240 visitExpression(node.right); |
| 241 } | 241 } |
| 242 | 242 |
| 243 visitFunctionDeclaration(FunctionDeclaration node) { | 243 visitFunctionDeclaration(FunctionDeclaration node) { |
| 244 // The function variable is final, hence cannot be merged. | 244 // The function variable is final, hence cannot be merged. |
| 245 ignoreVariable(node.variable); | 245 ignoreVariable(node.variable); |
| 246 visitStatement(node.next); | 246 visitStatement(node.next); |
| 247 // Do not traverse inner function. | |
| 248 } | |
| 249 | |
| 250 visitFunctionExpression(FunctionExpression node) { | |
| 251 // Do not traverse inner function. | |
| 252 } | |
| 253 | |
| 254 visitFunctionDefinition(FunctionDefinition node) { | |
| 255 // Function parameters are treated as write operations at the entry point, | |
| 256 // so they can potentially be merged with other copies of the parameter. | |
| 257 // Note that function parameters always have distinct source variables, | |
| 258 // so we don't risk accidentally merging two parameters. | |
| 259 node.parameters.forEach(write); | |
| 260 visitStatement(node.body); | |
| 261 } | |
| 262 | |
| 263 visitConstructorDefinition(ConstructorDefinition node) { | |
| 264 node.parameters.forEach(write); | |
| 265 node.initializers.forEach(visitInitializer); | |
| 266 visitStatement(node.body); | |
| 267 } | 247 } |
| 268 } | 248 } |
| 269 | 249 |
| 270 /// Computes liveness information of the given control-flow graph. | 250 /// Computes liveness information of the given control-flow graph. |
| 271 /// | 251 /// |
| 272 /// The results are stored in [Block.liveIn] and [Block.liveOut]. | 252 /// The results are stored in [Block.liveIn] and [Block.liveOut]. |
| 273 void _computeLiveness(List<Block> blocks) { | 253 void _computeLiveness(List<Block> blocks) { |
| 274 // We use a LIFO queue as worklist. Blocks are given in AST order, so by | 254 // We use a LIFO queue as worklist. Blocks are given in AST order, so by |
| 275 // inserting them in this order, we initially visit them backwards, which | 255 // inserting them in this order, we initially visit them backwards, which |
| 276 // is a good ordering. | 256 // is a good ordering. |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 467 subst[v1] = v1; | 447 subst[v1] = v1; |
| 468 } else { | 448 } else { |
| 469 subst[v1] = potential.first; | 449 subst[v1] = potential.first; |
| 470 } | 450 } |
| 471 } | 451 } |
| 472 | 452 |
| 473 return subst; | 453 return subst; |
| 474 } | 454 } |
| 475 | 455 |
| 476 /// Performs variable substitution and removes redundant assignments. | 456 /// Performs variable substitution and removes redundant assignments. |
| 477 class SubstituteVariables extends RecursiveVisitor { | 457 class SubstituteVariables extends RecursiveTransformer { |
| 478 | 458 |
| 479 Map<Variable, Variable> mapping; | 459 Map<Variable, Variable> mapping; |
| 480 | 460 |
| 481 SubstituteVariables(this.mapping); | 461 SubstituteVariables(this.mapping); |
| 482 | 462 |
| 483 Variable replaceRead(Variable variable) { | 463 Variable replaceRead(Variable variable) { |
| 484 Variable w = mapping[variable]; | 464 Variable w = mapping[variable]; |
| 485 if (w == null) return variable; // Skip ignored variables. | 465 if (w == null) return variable; // Skip ignored variables. |
| 486 w.readCount++; | 466 w.readCount++; |
| 487 variable.readCount--; | 467 variable.readCount--; |
| 488 return w; | 468 return w; |
| 489 } | 469 } |
| 490 | 470 |
| 491 Variable replaceWrite(Variable variable) { | 471 Variable replaceWrite(Variable variable) { |
| 492 Variable w = mapping[variable]; | 472 Variable w = mapping[variable]; |
| 493 if (w == null) return variable; // Skip ignored variables. | 473 if (w == null) return variable; // Skip ignored variables. |
| 494 w.writeCount++; | 474 w.writeCount++; |
| 495 variable.writeCount--; | 475 variable.writeCount--; |
| 496 return w; | 476 return w; |
| 497 } | 477 } |
| 498 | 478 |
| 499 void replaceParameters(List<Variable> parameters) { | 479 void apply(RootNode node) { |
| 500 for (int i = 0; i < parameters.length; ++i) { | 480 for (int i=0; i < node.parameters.length; i++) { |
|
Kevin Millikin (Google)
2015/04/08 15:10:15
Hmmm. It was better before.
asgerf
2015/04/09 09:58:23
Well the diff is a bit misleading here.
I'm not s
Kevin Millikin (Google)
2015/04/09 10:56:32
No, there should be a space around the binary assi
asgerf
2015/04/09 11:01:18
Ah. Done.
| |
| 501 parameters[i] = replaceWrite(parameters[i]); | 481 node.parameters[i] = replaceWrite(node.parameters[i]); |
| 502 } | 482 } |
| 483 node.replaceEachBody(visitStatement); | |
| 503 } | 484 } |
| 504 | 485 |
| 505 visitVariableUse(VariableUse node) { | 486 @override |
| 506 node.variable = replaceRead(node.variable); | 487 void visitInnerFunction(FunctionDefinition node) { |
| 488 // Do nothing. Inner functions are traversed in VariableMerger. | |
| 507 } | 489 } |
| 508 | 490 |
| 509 visitFunctionDefinition(FunctionDefinition node) { | 491 Expression visitVariableUse(VariableUse node) { |
| 510 replaceParameters(node.parameters); | 492 node.variable = replaceRead(node.variable); |
| 511 node.body = visitStatement(node.body); | 493 return node; |
| 512 } | 494 } |
| 513 | 495 |
| 514 visitConstructorDefinition(ConstructorDefinition node) { | |
| 515 replaceParameters(node.parameters); | |
| 516 node.initializers.forEach(visitInitializer); | |
| 517 node.body = visitStatement(node.body); | |
| 518 } | |
| 519 | |
| 520 visitFieldInitializer(FieldInitializer node) { | |
| 521 node.body = visitStatement(node.body); | |
| 522 } | |
| 523 | |
| 524 visitSuperInitializer(SuperInitializer node) { | |
| 525 for (int i = 0; i<node.arguments.length; ++i) { | |
| 526 node.arguments[i] = visitStatement(node.arguments[i]); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 // Statement visitors should return the transformed statement so we | |
| 531 // can remove redundant assignments. | |
| 532 Statement visitStatement(Statement node) => super.visitStatement(node); | |
| 533 | |
| 534 Statement visitAssign(Assign node) { | 496 Statement visitAssign(Assign node) { |
| 535 node.variable = replaceWrite(node.variable); | 497 node.variable = replaceWrite(node.variable); |
| 536 | 498 |
| 537 visitExpression(node.value); | 499 visitExpression(node.value); |
| 538 node.next = visitStatement(node.next); | 500 node.next = visitStatement(node.next); |
| 539 | 501 |
| 540 // Remove assignments of form "x := x" | 502 // Remove assignments of form "x := x" |
| 541 if (node.value is VariableUse) { | 503 if (node.value is VariableUse) { |
| 542 VariableUse value = node.value; | 504 VariableUse value = node.value; |
| 543 if (value.variable == node.variable) { | 505 if (value.variable == node.variable) { |
| 544 value.variable.readCount--; | 506 value.variable.readCount--; |
| 545 node.variable.writeCount--; | 507 node.variable.writeCount--; |
| 546 return node.next; | 508 return node.next; |
| 547 } | 509 } |
| 548 } | 510 } |
| 549 | 511 |
| 550 return node; | 512 return node; |
| 551 } | 513 } |
| 552 | |
| 553 Statement visitLabeledStatement(LabeledStatement node) { | |
| 554 node.body = visitStatement(node.body); | |
| 555 node.next = visitStatement(node.next); | |
| 556 return node; | |
| 557 } | |
| 558 | |
| 559 Statement visitReturn(Return node) { | |
| 560 visitExpression(node.value); | |
| 561 return node; | |
| 562 } | |
| 563 | |
| 564 Statement visitBreak(Break node) { | |
| 565 return node; | |
| 566 } | |
| 567 | |
| 568 Statement visitContinue(Continue node) { | |
| 569 return node; | |
| 570 } | |
| 571 | |
| 572 Statement visitIf(If node) { | |
| 573 visitExpression(node.condition); | |
| 574 node.thenStatement = visitStatement(node.thenStatement); | |
| 575 node.elseStatement = visitStatement(node.elseStatement); | |
| 576 return node; | |
| 577 } | |
| 578 | |
| 579 Statement visitWhileTrue(WhileTrue node) { | |
| 580 node.body = visitStatement(node.body); | |
| 581 return node; | |
| 582 } | |
| 583 | |
| 584 Statement visitWhileCondition(WhileCondition node) { | |
| 585 visitExpression(node.condition); | |
| 586 node.body = visitStatement(node.body); | |
| 587 node.next = visitStatement(node.next); | |
| 588 return node; | |
| 589 } | |
| 590 | |
| 591 Statement visitFunctionDeclaration(FunctionDeclaration node) { | |
| 592 node.next = visitStatement(node.next); | |
| 593 return node; | |
| 594 } | |
| 595 | |
| 596 Statement visitExpressionStatement(ExpressionStatement node) { | |
| 597 visitExpression(node.expression); | |
| 598 node.next = visitStatement(node.next); | |
| 599 return node; | |
| 600 } | |
| 601 | |
| 602 Statement visitTry(Try node) { | |
| 603 node.tryBody = visitStatement(node.tryBody); | |
| 604 node.catchBody = visitStatement(node.catchBody); | |
| 605 return node; | |
| 606 } | |
| 607 | |
| 608 Statement visitSetField(SetField node) { | |
| 609 visitExpression(node.object); | |
| 610 visitExpression(node.value); | |
| 611 node.next = visitStatement(node.next); | |
| 612 return node; | |
| 613 } | |
| 614 } | 514 } |
| OLD | NEW |