Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(356)

Side by Side Diff: pkg/compiler/lib/src/tree_ir/optimization/variable_merger.dart

Issue 1625643002: dart2js cps: Make register allocation prioritize phi elimination. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Clean up Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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;
19
20 VariableMerger({this.minifying});
Kevin Millikin (Google) 2016/01/26 08:27:26 Is this an optional parameter, or merely named? I
asgerf 2016/01/26 09:07:38 Set default to false.
21
18 void rewrite(FunctionDefinition node) { 22 void rewrite(FunctionDefinition node) {
19 BlockGraphBuilder builder = new BlockGraphBuilder(); 23 BlockGraphBuilder builder = new BlockGraphBuilder()..build(node);
20 builder.build(node);
21 _computeLiveness(builder.blocks); 24 _computeLiveness(builder.blocks);
22 Map<Variable, Variable> subst = 25 PriorityPairs priority = new PriorityPairs()..build(node);
23 _computeRegisterAllocation(builder.blocks, node.parameters); 26 Map<Variable, Variable> subst = _computeRegisterAllocation(
27 builder.blocks, node.parameters, priority, minifying: minifying);
24 new SubstituteVariables(subst).apply(node); 28 new SubstituteVariables(subst).apply(node);
25 } 29 }
26 } 30 }
27 31
28 /// A read or write access to a variable. 32 /// A read or write access to a variable.
29 class VariableAccess { 33 class VariableAccess {
30 Variable variable; 34 Variable variable;
31 bool isRead; 35 bool isRead;
32 bool get isWrite => !isRead; 36 bool get isWrite => !isRead;
33 37
(...skipping 200 matching lines...) Expand 10 before | Expand all | Expand 10 after
234 238
235 visitLogicalOperator(LogicalOperator node) { 239 visitLogicalOperator(LogicalOperator node) {
236 visitExpression(node.left); 240 visitExpression(node.left);
237 Block afterLeft = _currentBlock; 241 Block afterLeft = _currentBlock;
238 branchFrom(afterLeft); 242 branchFrom(afterLeft);
239 visitExpression(node.right); 243 visitExpression(node.right);
240 joinFrom(_currentBlock, afterLeft); 244 joinFrom(_currentBlock, afterLeft);
241 } 245 }
242 } 246 }
243 247
248 /// Collects prioritized variable pairs -- pairs that lead to significant code
249 /// reduction if merged into one variable.
250 ///
251 /// These arise from moving assigments `v1 = v2`, and compoundable assignments
252 /// `v1 = v2 [+] E` where [+] is a compoundable operator.
253 //
254 // TODO(asgerf): We could have a more fine-grained priority level. All pairs
255 // are treated as equally important, but some pairs can eliminate more than
256 // one assignment.
257 // Also, some assignments are more important to remove than others, as they
258 // can block a later optimization, such rewriting a loop, or removing the
259 // 'else' part of an 'if'.
260 //
261 class PriorityPairs extends RecursiveVisitor {
262 final Map<Variable, List<Variable>> _priority = <Variable, List<Variable>>{};
263
264 void build(FunctionDefinition node) {
265 visitStatement(node.body);
266 }
267
268 void _prioritize(Variable x, Variable y) {
269 _priority.putIfAbsent(x, () => new List<Variable>()).add(y);
270 _priority.putIfAbsent(y, () => new List<Variable>()).add(x);
271 }
272
273 visitAssign(Assign node) {
274 super.visitAssign(node);
275 Expression value = node.value;
276 if (value is VariableUse) {
277 _prioritize(node.variable, value.variable);
278 } else if (value is ApplyBuiltinOperator &&
279 isCompoundableOperator(value.operator) &&
280 value.arguments[0] is VariableUse) {
281 VariableUse use = value.arguments[0];
282 _prioritize(node.variable, use.variable);
283 }
284 }
285
286 /// Returns the other half of every priority pair containing [variable].
287 List<Variable> getPriorityPairsWith(Variable variable) {
288 return _priority[variable] ?? const <Variable>[];
289 }
290
291 bool hasPriorityPairs(Variable variable) {
292 return _priority.containsKey(variable);
293 }
294 }
295
244 /// Computes liveness information of the given control-flow graph. 296 /// Computes liveness information of the given control-flow graph.
245 /// 297 ///
246 /// The results are stored in [Block.liveIn] and [Block.liveOut]. 298 /// The results are stored in [Block.liveIn] and [Block.liveOut].
247 void _computeLiveness(List<Block> blocks) { 299 void _computeLiveness(List<Block> blocks) {
248 // We use a LIFO queue as worklist. Blocks are given in AST order, so by 300 // We use a LIFO queue as worklist. Blocks are given in AST order, so by
249 // inserting them in this order, we initially visit them backwards, which 301 // inserting them in this order, we initially visit them backwards, which
250 // is a good ordering. 302 // is a good ordering.
251 // The choice of LIFO for re-inserted blocks is currently arbitrary, 303 // The choice of LIFO for re-inserted blocks is currently arbitrary,
252 List<Block> worklist = new List<Block>.from(blocks); 304 List<Block> worklist = new List<Block>.from(blocks);
253 while (!worklist.isEmpty) { 305 while (!worklist.isEmpty) {
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
317 } 369 }
318 if (changed && !pred.inWorklist) { 370 if (changed && !pred.inWorklist) {
319 worklist.add(pred); 371 worklist.add(pred);
320 pred.inWorklist = true; 372 pred.inWorklist = true;
321 } 373 }
322 } 374 }
323 } 375 }
324 } 376 }
325 } 377 }
326 378
327 /// For testing purposes, this flag can be passed to merge variables that
328 /// originated from different source variables.
329 ///
330 /// Correctness should not depend on the fact that we only merge variables
331 /// originating from the same source variable. Setting this flag makes a bug
332 /// more likely to provoke a test case failure.
333 const bool NO_PRESERVE_VARS = const bool.fromEnvironment('NO_PRESERVE_VARS');
334
335 /// Based on liveness information, computes a map of variable substitutions to 379 /// Based on liveness information, computes a map of variable substitutions to
336 /// merge variables. 380 /// merge variables.
337 /// 381 ///
338 /// Constructs a register interference graph. This is an undirected graph of 382 /// Constructs a register interference graph. This is an undirected graph of
339 /// variables, with an edge between two variables if they cannot be merged 383 /// variables, with an edge between two variables if they cannot be merged
340 /// (because they are live simultaneously). 384 /// (because they are live simultaneously).
341 /// 385 ///
342 /// We then compute a graph coloring, where the color of a node denotes which 386 /// We then compute a graph coloring, where the color of a node denotes which
343 /// variable it will be substituted by. 387 /// variable it will be substituted by.
344 ///
345 /// We never merge variables that originated from distinct source variables,
346 /// so we build a separate register interference graph for each source variable.
347 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks, 388 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks,
348 List<Variable> parameters) { 389 List<Variable> parameters,
390 PriorityPairs priority,
391 {bool minifying}) {
349 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{}; 392 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{};
350 393
351 /// Group for the given variable. We attempt to merge variables in the same 394 bool allowUnmotivatedMerge(Variable x, Variable y) {
352 /// group. 395 if (minifying) return true;
353 /// By default, variables are grouped based on their source variable name, 396 // Do not allow merging temporaries with named variables if they are
354 /// but this can be disabled for testing purposes. 397 // not connected by a phi. That would leads to confusing mergings like:
355 String group(Variable variable) { 398 // var v0 = receiver.length;
356 if (NO_PRESERVE_VARS) return ''; 399 // ==>
357 // Group variables based on the source variable's name, not its element, 400 // receiver = receiver.length;
358 // so if multiple locals are declared with the same name, they will 401 return x.element?.name == y.element?.name;
359 // map to the same (hoisted) variable in the output. 402 }
360 return variable.element == null ? '' : variable.element.name; 403
404 bool allowPhiMerge(Variable x, Variable y) {
405 if (minifying) return true;
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
408 // same value, so it is not that confusing that they get the same name.
409 return x.element == null ||
410 y.element == null ||
411 x.element.name == y.element.name;
361 } 412 }
362 413
363 Set<Variable> empty = new Set<Variable>(); 414 Set<Variable> empty = new Set<Variable>();
364 415
365 // 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
366 // 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).
367 for (Block block in blocks) { 418 for (Block block in blocks) {
368 // Group the liveOut set by source variable. 419 // Track the live set while traversing the block.
369 Map<String, Set<Variable>> liveOut = <String, Set<Variable>>{}; 420 Set<Variable> live = new Set<Variable>();
370 for (Variable variable in block.liveOut) { 421 for (Variable variable in block.liveOut) {
371 liveOut.putIfAbsent( 422 live.add(variable);
372 group(variable),
373 () => new Set<Variable>()).add(variable);
374 interference.putIfAbsent(variable, () => new Set<Variable>()); 423 interference.putIfAbsent(variable, () => new Set<Variable>());
375 } 424 }
376 // Get variables that are live at the catch block. 425 // Get variables that are live at the catch block.
377 Set<Variable> liveCatch = block.catchBlock != null 426 Set<Variable> liveCatch = block.catchBlock != null
378 ? block.catchBlock.liveIn 427 ? block.catchBlock.liveIn
379 : empty; 428 : empty;
380 // Add edges for each variable being assigned here. 429 // Add edges for each variable being assigned here.
381 for (VariableAccess access in block.accesses.reversed) { 430 for (VariableAccess access in block.accesses.reversed) {
382 Variable variable = access.variable; 431 Variable variable = access.variable;
383 interference.putIfAbsent(variable, () => new Set<Variable>()); 432 interference.putIfAbsent(variable, () => new Set<Variable>());
384 Set<Variable> live =
385 liveOut.putIfAbsent(group(variable), () => new Set<Variable>());
386 if (access.isRead) { 433 if (access.isRead) {
387 live.add(variable); 434 live.add(variable);
388 } else { 435 } else {
389 if (!liveCatch.contains(variable)) { 436 if (!liveCatch.contains(variable)) {
390 // Assignment to a variable that is not live in the catch block. 437 // Assignment to a variable that is not live in the catch block.
391 live.remove(variable); 438 live.remove(variable);
392 } 439 }
393 for (Variable other in live) { 440 for (Variable other in live) {
394 interference[variable].add(other); 441 interference[variable].add(other);
395 interference[other].add(variable); 442 interference[other].add(variable);
396 } 443 }
397 } 444 }
398 } 445 }
399 } 446 }
400 447
401 // Sort the variables by descending degree. 448 // Sort the variables by descending degree.
402 // The most constrained variables will be assigned a color first. 449 // The most constrained variables will be assigned a color first.
403 List<Variable> variables = interference.keys.toList(); 450 List<Variable> variables = interference.keys.toList();
404 variables.sort((x, y) => interference[y].length - interference[x].length); 451 variables.sort((x, y) => interference[y].length - interference[x].length);
405 452
406 Map<String, List<Variable>> registers = <String, List<Variable>>{}; 453 List<Variable> registers = <Variable>[];
407 Map<Variable, Variable> subst = <Variable, Variable>{}; 454 Map<Variable, Variable> subst = <Variable, Variable>{};
408 455
409 // Parameters are special in that they must have a ParameterElement and 456 /// Called when [variable] has been assigned [target] as its register/color.
410 // cannot be merged with each other. Ensure that they are not substituted. 457 /// Will immediately try to satisfy its priority pairs by assigning the same
411 // Other variables can still be substituted by a parameter. 458 /// color the other half of each pair.
459 void searchPriorityPairs(Variable variable, Variable target) {
460 if (!priority.hasPriorityPairs(variable)) {
461 return; // Most variables (around 90%) do not have priority pairs.
462 }
463 List<Variable> worklist = <Variable>[variable];
464 while (worklist.isNotEmpty) {
465 Variable v1 = worklist.removeLast();
466 for (Variable v2 in priority.getPriorityPairsWith(v1)) {
467 // If v2 already has a color, we cannot change it.
468 if (subst.containsKey(v2)) continue;
469
470 // Do not merge differently named variables.
471 if (!allowPhiMerge(v1, v2)) continue;
472
473 // Ensure the graph coloring remains valid. If a neighbour of v2 already
474 // has the desired color, we cannot assign the same color to v2.
475 if (interference[v2].any((v3) => subst[v3] == target)) continue;
476
477 subst[v2] = target;
478 target.element ??= v2.element; // Preserve the name.
479 worklist.add(v2);
480 }
481 }
482 }
483
484 void assignRegister(Variable variable, Variable registerRepresentative) {
485 subst[variable] = registerRepresentative;
486 // Ensure this register is never assigned to a variable with another name.
487 // This also ensures that named variables keep their name when merged
488 // with a temporary.
489 registerRepresentative.element ??= variable.element;
490 searchPriorityPairs(variable, registerRepresentative);
491 }
492
493 void assignNewRegister(Variable variable) {
494 registers.add(variable);
495 subst[variable] = variable;
496 searchPriorityPairs(variable, variable);
497 }
498
499 // Parameters cannot be merged with each other. Ensure that they are not
500 // substituted. Other variables can still be substituted by a parameter.
412 for (Variable parameter in parameters) { 501 for (Variable parameter in parameters) {
413 if (parameter.isCaptured) continue; 502 if (parameter.isCaptured) continue;
503 registers.add(parameter);
414 subst[parameter] = parameter; 504 subst[parameter] = parameter;
415 registers[group(parameter)] = <Variable>[parameter];
416 } 505 }
417 506
507 // Try to merge parameters with locals to eliminate phis.
508 for (Variable parameter in parameters) {
509 searchPriorityPairs(parameter, parameter);
510 }
511
512 v1loop:
418 for (Variable v1 in variables) { 513 for (Variable v1 in variables) {
419 // Parameters have already been assigned a substitute; skip those. 514 // Ignore if the variable has already been assigned a register.
420 if (subst.containsKey(v1)) continue; 515 if (subst.containsKey(v1)) continue;
421 516
422 List<Variable> register = registers[group(v1)];
423
424 // Optimization: For the first variable in a group, allocate a new color
425 // without iterating over its interference edges.
426 if (register == null) {
427 registers[group(v1)] = <Variable>[v1];
428 subst[v1] = v1;
429 continue;
430 }
431
432 // Optimization: If there are no interference edges for this variable, 517 // Optimization: If there are no interference edges for this variable,
433 // assign it the first color without copying the register list. 518 // find a color for it without copying the register list.
434 Set<Variable> interferenceSet = interference[v1]; 519 Set<Variable> interferenceSet = interference[v1];
435 if (interferenceSet.isEmpty) { 520 if (interferenceSet.isEmpty) {
436 subst[v1] = register[0]; 521 // Use the first register where naming constraints allow the merge.
522 for (Variable v2 in registers) {
523 if (allowUnmotivatedMerge(v1, v2)) {
524 assignRegister(v1, v2);
525 continue v1loop;
526 }
527 }
528 // No register allows merging with this one, create a new register.
529 assignNewRegister(v1);
437 continue; 530 continue;
438 } 531 }
439 532
440 // Find an unused color. 533 // Find an unused color.
441 Set<Variable> potential = new Set<Variable>.from(register); 534 Set<Variable> potential = new Set<Variable>.from(
535 registers.where((v2) => allowUnmotivatedMerge(v1, v2)));
442 for (Variable v2 in interferenceSet) { 536 for (Variable v2 in interferenceSet) {
443 Variable v2subst = subst[v2]; 537 Variable v2subst = subst[v2];
444 if (v2subst != null) { 538 if (v2subst != null) {
445 potential.remove(v2subst); 539 potential.remove(v2subst);
446 if (potential.isEmpty) break; 540 if (potential.isEmpty) break;
447 } 541 }
448 } 542 }
449 543
450 if (potential.isEmpty) { 544 if (potential.isEmpty) {
451 // If no free color was found, add this variable as a new color. 545 // If no free color was found, add this variable as a new color.
452 register.add(v1); 546 assignNewRegister(v1);
453 subst[v1] = v1;
454 } else { 547 } else {
455 subst[v1] = potential.first; 548 assignRegister(v1, potential.first);
456 } 549 }
457 } 550 }
458 551
459 return subst; 552 return subst;
460 } 553 }
461 554
462 /// Performs variable substitution and removes redundant assignments. 555 /// Performs variable substitution and removes redundant assignments.
463 class SubstituteVariables extends RecursiveTransformer { 556 class SubstituteVariables extends RecursiveTransformer {
464 557
465 Map<Variable, Variable> mapping; 558 Map<Variable, Variable> mapping;
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
514 node.expression = visitExpression(node.expression); 607 node.expression = visitExpression(node.expression);
515 node.next = visitStatement(node.next); 608 node.next = visitStatement(node.next);
516 if (node.expression is VariableUse) { 609 if (node.expression is VariableUse) {
517 VariableUse use = node.expression; 610 VariableUse use = node.expression;
518 --use.variable.readCount; 611 --use.variable.readCount;
519 return node.next; 612 return node.next;
520 } 613 }
521 return node; 614 return node;
522 } 615 }
523 } 616 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/js_backend/codegen/task.dart ('k') | tests/compiler/dart2js/cps_ir/expected/redundant_condition.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698