| 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; | 7 import 'optimization.dart' show Pass; |
| 8 import '../tree_ir_nodes.dart'; | 8 import '../tree_ir_nodes.dart'; |
| 9 | 9 |
| 10 /// Merges variables based on liveness and source variable information. | 10 /// Merges variables based on liveness and source variable information. |
| 11 /// | 11 /// |
| 12 /// This phase cleans up artifacts introduced by the translation through CPS, | 12 /// This phase cleans up artifacts introduced by the translation through CPS, |
| 13 /// where each source variable is translated into several copies. The copies | 13 /// where each source variable is translated into several copies. The copies |
| 14 /// are merged again when they are not live simultaneously. | 14 /// are merged again when they are not live simultaneously. |
| 15 class VariableMerger implements Pass { | 15 class VariableMerger implements Pass { |
| 16 String get passName => 'Variable merger'; | 16 String get passName => 'Variable merger'; |
| 17 | 17 |
| 18 final bool minifying; | 18 final bool minifying; |
| 19 | 19 |
| 20 VariableMerger({this.minifying: false}); | 20 VariableMerger({this.minifying: false}); |
| 21 | 21 |
| 22 void rewrite(FunctionDefinition node) { | 22 void rewrite(FunctionDefinition node) { |
| 23 BlockGraphBuilder builder = new BlockGraphBuilder()..build(node); | 23 BlockGraphBuilder builder = new BlockGraphBuilder()..build(node); |
| 24 _computeLiveness(builder.blocks); | 24 _computeLiveness(builder.blocks); |
| 25 PriorityPairs priority = new PriorityPairs()..build(node); | 25 PriorityPairs priority = new PriorityPairs()..build(node); |
| 26 Map<Variable, Variable> subst = _computeRegisterAllocation( | 26 Map<Variable, Variable> subst = _computeRegisterAllocation( |
| 27 builder.blocks, node.parameters, priority, minifying: minifying); | 27 builder.blocks, node.parameters, priority, |
| 28 minifying: minifying); |
| 28 new SubstituteVariables(subst).apply(node); | 29 new SubstituteVariables(subst).apply(node); |
| 29 } | 30 } |
| 30 } | 31 } |
| 31 | 32 |
| 32 /// A read or write access to a variable. | 33 /// A read or write access to a variable. |
| 33 class VariableAccess { | 34 class VariableAccess { |
| 34 Variable variable; | 35 Variable variable; |
| 35 bool isRead; | 36 bool isRead; |
| 36 bool get isWrite => !isRead; | 37 bool get isWrite => !isRead; |
| 37 | 38 |
| (...skipping 231 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 269 _priority.putIfAbsent(x, () => new List<Variable>()).add(y); | 270 _priority.putIfAbsent(x, () => new List<Variable>()).add(y); |
| 270 _priority.putIfAbsent(y, () => new List<Variable>()).add(x); | 271 _priority.putIfAbsent(y, () => new List<Variable>()).add(x); |
| 271 } | 272 } |
| 272 | 273 |
| 273 visitAssign(Assign node) { | 274 visitAssign(Assign node) { |
| 274 super.visitAssign(node); | 275 super.visitAssign(node); |
| 275 Expression value = node.value; | 276 Expression value = node.value; |
| 276 if (value is VariableUse) { | 277 if (value is VariableUse) { |
| 277 _prioritize(node.variable, value.variable); | 278 _prioritize(node.variable, value.variable); |
| 278 } else if (value is ApplyBuiltinOperator && | 279 } else if (value is ApplyBuiltinOperator && |
| 279 isCompoundableOperator(value.operator) && | 280 isCompoundableOperator(value.operator) && |
| 280 value.arguments[0] is VariableUse) { | 281 value.arguments[0] is VariableUse) { |
| 281 VariableUse use = value.arguments[0]; | 282 VariableUse use = value.arguments[0]; |
| 282 _prioritize(node.variable, use.variable); | 283 _prioritize(node.variable, use.variable); |
| 283 } | 284 } |
| 284 } | 285 } |
| 285 | 286 |
| 286 /// Returns the other half of every priority pair containing [variable]. | 287 /// Returns the other half of every priority pair containing [variable]. |
| 287 List<Variable> getPriorityPairsWith(Variable variable) { | 288 List<Variable> getPriorityPairsWith(Variable variable) { |
| 288 return _priority[variable] ?? const <Variable>[]; | 289 return _priority[variable] ?? const <Variable>[]; |
| 289 } | 290 } |
| 290 | 291 |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 378 | 379 |
| 379 /// Based on liveness information, computes a map of variable substitutions to | 380 /// Based on liveness information, computes a map of variable substitutions to |
| 380 /// merge variables. | 381 /// merge variables. |
| 381 /// | 382 /// |
| 382 /// Constructs a register interference graph. This is an undirected graph of | 383 /// Constructs a register interference graph. This is an undirected graph of |
| 383 /// variables, with an edge between two variables if they cannot be merged | 384 /// variables, with an edge between two variables if they cannot be merged |
| 384 /// (because they are live simultaneously). | 385 /// (because they are live simultaneously). |
| 385 /// | 386 /// |
| 386 /// We then compute a graph coloring, where the color of a node denotes which | 387 /// We then compute a graph coloring, where the color of a node denotes which |
| 387 /// variable it will be substituted by. | 388 /// variable it will be substituted by. |
| 388 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks, | 389 Map<Variable, Variable> _computeRegisterAllocation( |
| 389 List<Variable> parameters, | 390 List<Block> blocks, List<Variable> parameters, PriorityPairs priority, |
| 390 PriorityPairs priority, | 391 {bool minifying}) { |
| 391 {bool minifying}) { | |
| 392 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{}; | 392 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{}; |
| 393 | 393 |
| 394 bool allowUnmotivatedMerge(Variable x, Variable y) { | 394 bool allowUnmotivatedMerge(Variable x, Variable y) { |
| 395 if (minifying) return true; | 395 if (minifying) return true; |
| 396 // Do not allow merging temporaries with named variables if they are | 396 // Do not allow merging temporaries with named variables if they are |
| 397 // not connected by a phi. That would leads to confusing mergings like: | 397 // not connected by a phi. That would leads to confusing mergings like: |
| 398 // var v0 = receiver.length; | 398 // var v0 = receiver.length; |
| 399 // ==> | 399 // ==> |
| 400 // receiver = receiver.length; | 400 // receiver = receiver.length; |
| 401 return x.element?.name == y.element?.name; | 401 return x.element?.name == y.element?.name; |
| 402 } | 402 } |
| 403 | 403 |
| 404 bool allowPhiMerge(Variable x, Variable y) { | 404 bool allowPhiMerge(Variable x, Variable y) { |
| 405 if (minifying) return true; | 405 if (minifying) return true; |
| 406 // Temporaries may be merged with a named variable if this eliminates a phi. | 406 // Temporaries may be merged with a named variable if this eliminates a phi. |
| 407 // The presence of the phi implies that the two variables can contain the | 407 // The presence of the phi implies that the two variables can contain the |
| 408 // same value, so it is not that confusing that they get the same name. | 408 // same value, so it is not that confusing that they get the same name. |
| 409 return x.element == null || | 409 return x.element == null || |
| 410 y.element == null || | 410 y.element == null || |
| 411 x.element.name == y.element.name; | 411 x.element.name == y.element.name; |
| 412 } | 412 } |
| 413 | 413 |
| 414 Set<Variable> empty = new Set<Variable>(); | 414 Set<Variable> empty = new Set<Variable>(); |
| 415 | 415 |
| 416 // At the assignment to a variable x, add an edge to every variable that is | 416 // At the assignment to a variable x, add an edge to every variable that is |
| 417 // live after the assignment (if it came from the same source variable). | 417 // live after the assignment (if it came from the same source variable). |
| 418 for (Block block in blocks) { | 418 for (Block block in blocks) { |
| 419 // Track the live set while traversing the block. | 419 // Track the live set while traversing the block. |
| 420 Set<Variable> live = new Set<Variable>(); | 420 Set<Variable> live = new Set<Variable>(); |
| 421 for (Variable variable in block.liveOut) { | 421 for (Variable variable in block.liveOut) { |
| 422 live.add(variable); | 422 live.add(variable); |
| 423 interference.putIfAbsent(variable, () => new Set<Variable>()); | 423 interference.putIfAbsent(variable, () => new Set<Variable>()); |
| 424 } | 424 } |
| 425 // Get variables that are live at the catch block. | 425 // Get variables that are live at the catch block. |
| 426 Set<Variable> liveCatch = block.catchBlock != null | 426 Set<Variable> liveCatch = |
| 427 ? block.catchBlock.liveIn | 427 block.catchBlock != null ? block.catchBlock.liveIn : empty; |
| 428 : empty; | |
| 429 // Add edges for each variable being assigned here. | 428 // Add edges for each variable being assigned here. |
| 430 for (VariableAccess access in block.accesses.reversed) { | 429 for (VariableAccess access in block.accesses.reversed) { |
| 431 Variable variable = access.variable; | 430 Variable variable = access.variable; |
| 432 interference.putIfAbsent(variable, () => new Set<Variable>()); | 431 interference.putIfAbsent(variable, () => new Set<Variable>()); |
| 433 if (access.isRead) { | 432 if (access.isRead) { |
| 434 live.add(variable); | 433 live.add(variable); |
| 435 } else { | 434 } else { |
| 436 if (!liveCatch.contains(variable)) { | 435 if (!liveCatch.contains(variable)) { |
| 437 // Assignment to a variable that is not live in the catch block. | 436 // Assignment to a variable that is not live in the catch block. |
| 438 live.remove(variable); | 437 live.remove(variable); |
| (...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 502 if (parameter.isCaptured) continue; | 501 if (parameter.isCaptured) continue; |
| 503 registers.add(parameter); | 502 registers.add(parameter); |
| 504 subst[parameter] = parameter; | 503 subst[parameter] = parameter; |
| 505 } | 504 } |
| 506 | 505 |
| 507 // Try to merge parameters with locals to eliminate phis. | 506 // Try to merge parameters with locals to eliminate phis. |
| 508 for (Variable parameter in parameters) { | 507 for (Variable parameter in parameters) { |
| 509 searchPriorityPairs(parameter, parameter); | 508 searchPriorityPairs(parameter, parameter); |
| 510 } | 509 } |
| 511 | 510 |
| 512 v1loop: | 511 v1loop: for (Variable v1 in variables) { |
| 513 for (Variable v1 in variables) { | |
| 514 // Ignore if the variable has already been assigned a register. | 512 // Ignore if the variable has already been assigned a register. |
| 515 if (subst.containsKey(v1)) continue; | 513 if (subst.containsKey(v1)) continue; |
| 516 | 514 |
| 517 // Optimization: If there are no interference edges for this variable, | 515 // Optimization: If there are no interference edges for this variable, |
| 518 // find a color for it without copying the register list. | 516 // find a color for it without copying the register list. |
| 519 Set<Variable> interferenceSet = interference[v1]; | 517 Set<Variable> interferenceSet = interference[v1]; |
| 520 if (interferenceSet.isEmpty) { | 518 if (interferenceSet.isEmpty) { |
| 521 // Use the first register where naming constraints allow the merge. | 519 // Use the first register where naming constraints allow the merge. |
| 522 for (Variable v2 in registers) { | 520 for (Variable v2 in registers) { |
| 523 if (allowUnmotivatedMerge(v1, v2)) { | 521 if (allowUnmotivatedMerge(v1, v2)) { |
| 524 assignRegister(v1, v2); | 522 assignRegister(v1, v2); |
| 525 continue v1loop; | 523 continue v1loop; |
| 526 } | 524 } |
| 527 } | 525 } |
| 528 // No register allows merging with this one, create a new register. | 526 // No register allows merging with this one, create a new register. |
| 529 assignNewRegister(v1); | 527 assignNewRegister(v1); |
| 530 continue; | 528 continue; |
| 531 } | 529 } |
| 532 | 530 |
| 533 // Find an unused color. | 531 // Find an unused color. |
| 534 Set<Variable> potential = new Set<Variable>.from( | 532 Set<Variable> potential = new Set<Variable>.from( |
| 535 registers.where((v2) => allowUnmotivatedMerge(v1, v2))); | 533 registers.where((v2) => allowUnmotivatedMerge(v1, v2))); |
| 536 for (Variable v2 in interferenceSet) { | 534 for (Variable v2 in interferenceSet) { |
| 537 Variable v2subst = subst[v2]; | 535 Variable v2subst = subst[v2]; |
| 538 if (v2subst != null) { | 536 if (v2subst != null) { |
| 539 potential.remove(v2subst); | 537 potential.remove(v2subst); |
| 540 if (potential.isEmpty) break; | 538 if (potential.isEmpty) break; |
| 541 } | 539 } |
| 542 } | 540 } |
| 543 | 541 |
| 544 if (potential.isEmpty) { | 542 if (potential.isEmpty) { |
| 545 // If no free color was found, add this variable as a new color. | 543 // If no free color was found, add this variable as a new color. |
| 546 assignNewRegister(v1); | 544 assignNewRegister(v1); |
| 547 } else { | 545 } else { |
| 548 assignRegister(v1, potential.first); | 546 assignRegister(v1, potential.first); |
| 549 } | 547 } |
| 550 } | 548 } |
| 551 | 549 |
| 552 return subst; | 550 return subst; |
| 553 } | 551 } |
| 554 | 552 |
| 555 /// Performs variable substitution and removes redundant assignments. | 553 /// Performs variable substitution and removes redundant assignments. |
| 556 class SubstituteVariables extends RecursiveTransformer { | 554 class SubstituteVariables extends RecursiveTransformer { |
| 557 | |
| 558 Map<Variable, Variable> mapping; | 555 Map<Variable, Variable> mapping; |
| 559 | 556 |
| 560 SubstituteVariables(this.mapping); | 557 SubstituteVariables(this.mapping); |
| 561 | 558 |
| 562 Variable replaceRead(Variable variable) { | 559 Variable replaceRead(Variable variable) { |
| 563 Variable w = mapping[variable]; | 560 Variable w = mapping[variable]; |
| 564 if (w == null) return variable; // Skip ignored variables. | 561 if (w == null) return variable; // Skip ignored variables. |
| 565 w.readCount++; | 562 w.readCount++; |
| 566 variable.readCount--; | 563 variable.readCount--; |
| 567 return w; | 564 return w; |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 607 node.expression = visitExpression(node.expression); | 604 node.expression = visitExpression(node.expression); |
| 608 node.next = visitStatement(node.next); | 605 node.next = visitStatement(node.next); |
| 609 if (node.expression is VariableUse) { | 606 if (node.expression is VariableUse) { |
| 610 VariableUse use = node.expression; | 607 VariableUse use = node.expression; |
| 611 --use.variable.readCount; | 608 --use.variable.readCount; |
| 612 return node.next; | 609 return node.next; |
| 613 } | 610 } |
| 614 return node; | 611 return node; |
| 615 } | 612 } |
| 616 } | 613 } |
| OLD | NEW |