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 |