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

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

Issue 1007103003: cps-ir: Merge variables based on set-based liveness and graph coloring. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Missed a few comments Created 5 years, 8 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 library tree_ir.optimization.variable_merger;
6
7 import 'optimization.dart' show Pass, PassMixin;
8 import '../tree_ir_nodes.dart';
9 import '../../elements/elements.dart' show Local, ParameterElement;
10
11 /// Merges variables based on liveness and source variable information.
12 ///
13 /// This phase cleans up artifacts introduced by the translation through CPS,
14 /// where each source variable is translated into several copies. The copies
15 /// are merged again when they are not live simultaneously.
16 class VariableMerger extends RecursiveVisitor with PassMixin {
17 String get passName => 'Variable merger';
18
19 @override
20 void rewriteExecutableDefinition(ExecutableDefinition node) {
21 visitExecutableDefinition(node);
22 }
23
24 /// Rewrites the given function.
25 /// This is called for the outermost function and inner functions.
26 void rewriteFunction(ExecutableDefinition node) {
27 BlockGraphBuilder builder = new BlockGraphBuilder();
28 builder.visitExecutableDefinition(node);
29 _computeLiveness(builder.blocks);
30 Map<Variable, Variable> subst = _computeRegisterAllocation(builder.blocks);
31 new SubstituteVariables(subst).visitExecutableDefinition(node);
32 }
33
34 visitFunctionDefinition(FunctionDefinition node) {
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 }
48 }
49
50 /// A read or write access to a variable.
51 class VariableAccess {
52 Variable variable;
53 bool isRead;
54 bool get isWrite => !isRead;
55
56 VariableAccess.read(this.variable) : isRead = true;
57 VariableAccess.write(this.variable) : isRead = false;
58 }
59
60 /// Basic block in a control-flow graph.
61 class Block {
62 /// List of predecessors in the control-flow graph.
63 final List<Block> predecessors = <Block>[];
64
65 /// Entry to the catch block for the enclosing try, or `null`.
66 final Block catchBlock;
67
68 /// List of nodes with this block as [catchBlock].
69 final List<Block> catchPredecessors = <Block>[];
70
71 /// Sequence of read and write accesses in the block.
72 final List<VariableAccess> accesses = <VariableAccess>[];
73
74 /// Auxiliary fields used by the liveness analysis.
75 bool inWorklist = true;
76 Set<Variable> liveIn;
77 Set<Variable> liveOut = new Set<Variable>();
78 Set<Variable> gen = new Set<Variable>();
79 Set<Variable> kill = new Set<Variable>();
80
81 /// Adds a read operation to the block and updates gen/kill sets accordingly.
82 void addRead(Variable variable) {
Kevin Millikin (Google) 2015/04/07 14:03:00 I guess there should be a comment here reminding u
83 if (!kill.contains(variable)) {
84 gen.add(variable);
85 }
86 accesses.add(new VariableAccess.read(variable));
87 }
88
89 /// Adds a write operation to the block and updates gen/kill sets accordingly.
90 void addWrite(Variable variable) {
91 if (!gen.contains(variable)) {
92 kill.add(variable);
93 }
94 accesses.add(new VariableAccess.write(variable));
95 }
96
97 Block(this.catchBlock) {
98 if (catchBlock != null) {
99 catchBlock.catchPredecessors.add(this);
100 }
101 }
102 }
103
104 /// Builds a control-flow graph suitable for performing liveness analysis.
105 class BlockGraphBuilder extends RecursiveVisitor {
106 Map<Label, Block> _jumpTarget = <Label, Block>{};
107 Block _currentBlock;
108 List<Block> blocks = <Block>[];
109
110 /// Variables with an assignment that should be treated as final.
111 ///
112 /// Such variables cannot be merged with any other variables, so we exclude
113 /// them from the control-flow graph entirely.
114 Set<Variable> _ignoredVariables = new Set<Variable>();
115
116 BlockGraphBuilder() {
117 _currentBlock = newBlock();
118 }
119
120 /// Creates a new block with the current exception handler or [catchBlock]
121 /// if provided.
122 Block newBlock({Block catchBlock}) {
123 if (catchBlock == null && _currentBlock != null) {
124 catchBlock = _currentBlock.catchBlock;
125 }
126 Block block = new Block(catchBlock);
127 blocks.add(block);
128 return block;
129 }
130
131 /// Starts a new block after the end of [block].
132 void branchFrom(Block block, {Block catchBlock}) {
133 _currentBlock = newBlock(catchBlock: catchBlock)..predecessors.add(block);
134 }
135
136 /// Called when reading from [variable].
137 ///
138 /// Appends a read operation to the current basic block.
139 void read(Variable variable) {
140 if (variable.isCaptured) return;
141 if (_ignoredVariables.contains(variable)) return;
142 _currentBlock.addRead(variable);
143 }
144
145 /// Called when writing to [variable].
146 ///
147 /// Appends a write operation to the current basic block.
148 void write(Variable variable) {
149 if (variable.isCaptured) return;
150 if (_ignoredVariables.contains(variable)) return;
151 _currentBlock.addWrite(variable);
152 }
153
154 /// Called to indicate that [variable] should not be merged, and therefore
155 /// be excluded from the control-flow graph.
156 /// Subsequent calls to [read] and [write] will ignore it.
157 void ignoreVariable(Variable variable) {
158 _ignoredVariables.add(variable);
159 }
160
161 visitVariableUse(VariableUse node) {
162 read(node.variable);
163 }
164
165 visitAssign(Assign node) {
166 visitExpression(node.value);
167 write(node.variable);
168 visitStatement(node.next);
169 }
170
171 visitIf(If node) {
172 visitExpression(node.condition);
173 Block afterCondition = _currentBlock;
174 branchFrom(afterCondition);
175 visitStatement(node.thenStatement);
176 branchFrom(afterCondition);
177 visitStatement(node.elseStatement);
178 }
179
180 visitLabeledStatement(LabeledStatement node) {
181 Block join = _jumpTarget[node.label] = newBlock();
182 visitStatement(node.body); // visitBreak will add predecessors to join.
183 _currentBlock = join;
184 visitStatement(node.next);
185 }
186
187 visitBreak(Break node) {
188 _jumpTarget[node.target].predecessors.add(_currentBlock);
189 }
190
191 visitContinue(Continue node) {
192 _jumpTarget[node.target].predecessors.add(_currentBlock);
193 }
194
195 visitWhileTrue(WhileTrue node) {
196 Block join = _jumpTarget[node.label] = newBlock();
197 join.predecessors.add(_currentBlock);
198 _currentBlock = join;
199 visitStatement(node.body); // visitContinue will add predecessors to join.
200 }
201
202 visitWhileCondition(WhileCondition node) {
203 Block join = _jumpTarget[node.label] = newBlock();
204 join.predecessors.add(_currentBlock);
205 _currentBlock = join;
206 visitExpression(node.condition);
207 Block afterCondition = _currentBlock;
208 branchFrom(afterCondition);
209 visitStatement(node.body); // visitContinue will add predecessors to join.
210 branchFrom(afterCondition);
211 visitStatement(node.next);
212 }
213
214 visitTry(Try node) {
215 Block catchBlock = newBlock();
216 branchFrom(_currentBlock, catchBlock: catchBlock);
217 visitStatement(node.tryBody);
218 _currentBlock = catchBlock;
219 // Catch parameters cannot be hoisted to the top of the function, so to
220 // avoid complications with scoping, we do not attempt to merge them.
221 node.catchParameters.forEach(ignoreVariable);
222 visitStatement(node.catchBody);
223 }
224
225 visitConditional(Conditional node) {
226 visitExpression(node.condition);
227 // TODO(asgerf): When assignment expressions are added, this is no longer
228 // sound; then we need to handle as a branch.
229 visitExpression(node.thenExpression);
230 visitExpression(node.elseExpression);
231 }
232
233 visitLogicalOperator(LogicalOperator node) {
234 visitExpression(node.left);
235 // TODO(asgerf): When assignment expressions are added, this is no longer
236 // sound; then we need to handle as a branch.
237 visitExpression(node.right);
238 }
239
240 visitFunctionDeclaration(FunctionDeclaration node) {
241 // The function variable is final, hence cannot be merged.
242 ignoreVariable(node.variable);
243 visitStatement(node.next);
244 // Do not traverse inner function.
245 }
246
247 visitFunctionExpression(FunctionExpression node) {
248 // Do not traverse inner function.
249 }
250
251 visitFunctionDefinition(FunctionDefinition node) {
252 // Function parameters are treated as write operations at the entry point,
253 // so they can potentially be merged with other copies of the parameter.
254 // Note that function parameters always have distinct source variables,
255 // so we don't risk accidentally merging two parameters.
256 node.parameters.forEach(write);
257 visitStatement(node.body);
258 }
259
260 visitConstructorDefinition(ConstructorDefinition node) {
261 node.parameters.forEach(write);
262 node.initializers.forEach(visitInitializer);
263 visitStatement(node.body);
264 }
265 }
266
267 /// Computes liveness information of the given control-flow graph.
268 ///
269 /// The results are stored in [Block.liveIn] and [Block.liveOut].
270 void _computeLiveness(List<Block> blocks) {
271 // We use a LIFO queue as worklist. Blocks are given in AST order, so by
272 // inserting them in this order, we initially visit them backwards, which
273 // is a good ordering.
274 // The choice of LIFO for re-inserted blocks is currently arbitrary,
275 List<Block> worklist = new List<Block>.from(blocks);
276 while (!worklist.isEmpty) {
277 Block block = worklist.removeLast();
278 block.inWorklist = false;
279
280 bool changed = false;
281
282 // The liveIn set is computed as:
283 //
284 // liveIn = (liveOut - kill) + gen
285 //
286 // We do the computation in two steps:
287 //
288 // 1. liveIn = gen
289 // 2. liveIn += (liveOut - kill)
290 //
291 // However, since liveIn only grows, and gen never changes, we only have
292 // to do the first step at the first iteration. Moreover, the gen set is
293 // not needed anywhere else, so we don't even need to copy it.
294 if (block.liveIn == null) {
295 block.liveIn = block.gen;
296 block.gen = null;
297 changed = true;
298 }
299
300 // liveIn += (liveOut - kill)
301 for (Variable variable in block.liveOut) {
302 if (!block.kill.contains(variable)) {
303 if (block.liveIn.add(variable)) {
304 changed = true;
305 }
306 }
307 }
308
309 // If anything changed, propagate liveness backwards.
310 if (changed) {
311 // Propagate live variables to predecessors.
312 for (Block predecessor in block.predecessors) {
313 int lengthBeforeChange = predecessor.liveOut.length;
314 predecessor.liveOut.addAll(block.liveIn);
315 if (!predecessor.inWorklist &&
316 predecessor.liveOut.length != lengthBeforeChange) {
317 worklist.add(predecessor);
318 predecessor.inWorklist = true;
319 }
320 }
321
322 // Propagate live variables to catch predecessors.
323 for (Block pred in block.catchPredecessors) {
324 bool changed = false;
325 int lengthBeforeChange = pred.liveOut.length;
326 pred.liveOut.addAll(block.liveIn);
327 if (pred.liveOut.length != lengthBeforeChange) {
328 changed = true;
329 }
330 // Assigning to a variable that is live in the catch block, does not
331 // kill the variable, because we conservatively assume that an exception
332 // could be thrown immediately before the assignment.
333 // Therefore remove live variables from all kill sets inside the try.
334 // Since the kill set is only used to subtract live variables from a
335 // set, the analysis remains monotone.
336 lengthBeforeChange = pred.kill.length;
337 pred.kill.removeAll(block.liveIn);
338 if (pred.kill.length != lengthBeforeChange) {
339 changed = true;
340 }
341 if (changed && !pred.inWorklist) {
342 worklist.add(pred);
343 pred.inWorklist = true;
344 }
345 }
346 }
347 }
348 }
349
350 /// For testing purposes, this flag can be passed to merge variables that
351 /// originated from different source variables.
352 ///
353 /// Correctness should not depend on the fact that we only merge variable
354 /// originating from the same source variable. Setting this flag makes a bug
355 /// more likely to provoke a test case failure.
356 const bool NO_PRESERVE_VARS = const bool.fromEnvironment('NO_PRESERVE_VARS');
357
358 /// Based on liveness information, computes a map of variable substitutions to
359 /// merge variables.
360 ///
361 /// Constructs a register interference graph. This is an undirected graph of
362 /// variables, with an edge between two variables if they cannot be merged
363 /// (because they are live simultaneously).
364 ///
365 /// We then compute a graph coloring, where the color of a node denotes which
366 /// variable it will be substituted by.
367 ///
368 /// We never merge variables that originated from distinct source variables,
369 /// so we build a separate register interference graph for each source variable.
370 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks) {
371 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{};
372
373 /// Group for the given variable. We attempt to merge variables in the same
374 /// group.
375 /// By default, variables are grouped based on their source variable, but
376 /// this can be disabled for testing purposes.
377 Local group(Variable variable) {
378 if (NO_PRESERVE_VARS) {
379 // Parameters may not occur more than once in a parameter list,
380 // so except for parameters, we try to merge all variables.
381 return variable.element is ParameterElement ? variable.element : null;
382 }
383 return variable.element;
384 }
385
386 Set<Variable> empty = new Set<Variable>();
387
388 // At the assignment to a variable x, add an edge to every variable that is
389 // live after the assignment (if it came from the same source variable).
390 for (Block block in blocks) {
391 // Group the liveOut set by source variable.
392 Map<Local, Set<Variable>> liveOut = <Local, Set<Variable>>{};
393 for (Variable variable in block.liveOut) {
394 liveOut.putIfAbsent(
395 group(variable),
396 () => new Set<Variable>()).add(variable);
397 interference.putIfAbsent(variable, () => new Set<Variable>());
398 }
399 // Get variables that are live at the catch block.
400 Set<Variable> liveCatch = block.catchBlock != null
401 ? block.catchBlock.liveIn
402 : empty;
403 // Add edges for each variable being assigned here.
404 for (VariableAccess access in block.accesses.reversed) {
405 Variable variable = access.variable;
406 interference.putIfAbsent(variable, () => new Set<Variable>());
407 Set<Variable> live =
408 liveOut.putIfAbsent(group(variable), () => new Set<Variable>());
409 if (access.isRead) {
410 live.add(variable);
411 } else {
412 if (!liveCatch.contains(variable)) {
413 // Assignment to a variable that is not live in the catch block.
414 live.remove(variable);
415 }
416 for (Variable other in live) {
417 interference[variable].add(other);
418 interference[other].add(variable);
419 }
420 }
421 }
422 }
423
424 // Sort the variables by descending degree.
425 // The most constrained variables will be assigned a color first.
426 List<Variable> variables = interference.keys.toList();
427 variables.sort((x, y) => interference[y].length - interference[x].length);
428
429 Map<Local, List<Variable>> registers = <Local, List<Variable>>{};
430 Map<Variable, Variable> subst = <Variable, Variable>{};
431
432 for (Variable v1 in variables) {
433 List<Variable> register = registers[group(v1)];
434
435 // Optimization: For the first variable in a group, allocate a new color
436 // without iterating over its interference edges.
437 if (register == null) {
438 registers[group(v1)] = <Variable>[v1];
439 subst[v1] = v1;
440 continue;
441 }
442
443 // Optimization: If there are no inteference edges for this variable,
444 // assign it the first color without copying the register list.
445 Set<Variable> interferenceSet = interference[v1];
446 if (interferenceSet.isEmpty) {
447 subst[v1] = register[0];
448 continue;
449 }
450
451 // Find an unused color.
452 Set<Variable> potential = new Set<Variable>.from(register);
453 for (Variable v2 in interferenceSet) {
454 Variable v2subst = subst[v2];
455 if (v2subst != null) {
456 potential.remove(v2subst);
457 if (potential.isEmpty) break;
458 }
459 }
460
461 if (potential.isEmpty) {
462 // If no free color was found, add this variable as a new color.
463 register.add(v1);
464 subst[v1] = v1;
465 } else {
466 subst[v1] = potential.first;
467 }
468 }
469
470 return subst;
471 }
472
473 /// Performs variable substitution and removes redundant assignments.
474 class SubstituteVariables extends RecursiveVisitor {
475
476 Map<Variable, Variable> mapping;
477
478 SubstituteVariables(this.mapping);
479
480 Variable replaceRead(Variable variable) {
481 Variable w = mapping[variable];
482 if (w == null) return variable; // Skip ignored variables.
483 w.readCount++;
484 variable.readCount--;
485 return w;
486 }
487
488 Variable replaceWrite(Variable variable) {
489 Variable w = mapping[variable];
490 if (w == null) return variable; // Skip ignored variables.
491 w.writeCount++;
492 variable.writeCount--;
493 return w;
494 }
495
496 void replaceParameters(List<Variable> parameters) {
497 for (int i = 0; i < parameters.length; ++i) {
498 parameters[i] = replaceWrite(parameters[i]);
499 }
500 }
501
502 visitVariableUse(VariableUse node) {
503 node.variable = replaceRead(node.variable);
504 }
505
506 visitFunctionDefinition(FunctionDefinition node) {
507 replaceParameters(node.parameters);
508 node.body = visitStatement(node.body);
509 }
510
511 visitConstructorDefinition(ConstructorDefinition node) {
512 replaceParameters(node.parameters);
513 node.initializers.forEach(visitInitializer);
514 node.body = visitStatement(node.body);
515 }
516
517 visitFieldInitializer(FieldInitializer node) {
518 node.body = visitStatement(node.body);
519 }
520
521 visitSuperInitializer(SuperInitializer node) {
522 for (int i = 0; i<node.arguments.length; ++i) {
523 node.arguments[i] = visitStatement(node.arguments[i]);
524 }
525 }
526
527 // Statement visitors should return the transformed statement so we
528 // can remove redundant assignments.
529 Statement visitStatement(Statement node) => super.visitStatement(node);
530
531 Statement visitAssign(Assign node) {
532 node.variable = replaceWrite(node.variable);
533
534 visitExpression(node.value);
535 node.next = visitStatement(node.next);
536
537 // Remove assignments of form "x := x"
538 if (node.value is VariableUse) {
539 VariableUse value = node.value;
540 if (value.variable == node.variable) {
541 value.variable.readCount--;
542 node.variable.writeCount--;
543 return node.next;
544 }
545 }
546
547 return node;
548 }
549
550 Statement visitLabeledStatement(LabeledStatement node) {
551 node.body = visitStatement(node.body);
552 node.next = visitStatement(node.next);
553 return node;
554 }
555
556 Statement visitReturn(Return node) {
557 visitExpression(node.value);
558 return node;
559 }
560
561 Statement visitBreak(Break node) {
562 return node;
563 }
564
565 Statement visitContinue(Continue node) {
566 return node;
567 }
568
569 Statement visitIf(If node) {
570 visitExpression(node.condition);
571 node.thenStatement = visitStatement(node.thenStatement);
572 node.elseStatement = visitStatement(node.elseStatement);
573 return node;
574 }
575
576 Statement visitWhileTrue(WhileTrue node) {
577 node.body = visitStatement(node.body);
578 return node;
579 }
580
581 Statement visitWhileCondition(WhileCondition node) {
582 visitExpression(node.condition);
583 node.body = visitStatement(node.body);
584 node.next = visitStatement(node.next);
585 return node;
586 }
587
588 Statement visitFunctionDeclaration(FunctionDeclaration node) {
589 node.next = visitStatement(node.next);
590 return node;
591 }
592
593 Statement visitExpressionStatement(ExpressionStatement node) {
594 visitExpression(node.expression);
595 node.next = visitStatement(node.next);
596 return node;
597 }
598
599 Statement visitTry(Try node) {
600 node.tryBody = visitStatement(node.tryBody);
601 node.catchBody = visitStatement(node.catchBody);
602 return node;
603 }
604
605 Statement visitSetField(SetField node) {
606 visitExpression(node.object);
607 visitExpression(node.value);
608 node.next = visitStatement(node.next);
609 return node;
610 }
611 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/tree_ir/optimization/statement_rewriter.dart ('k') | pkg/compiler/lib/src/tree_ir/tree_ir_builder.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698