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

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: Rebase 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) {
83 // Operations are seen in forward order.
84 // If the read is not preceded by a write, then add it to the GEN set.
85 if (!kill.contains(variable)) {
86 gen.add(variable);
87 }
88 accesses.add(new VariableAccess.read(variable));
89 }
90
91 /// Adds a write operation to the block and updates gen/kill sets accordingly.
92 void addWrite(Variable variable) {
93 // If the write is not preceded by a read, then add it to the KILL set.
94 if (!gen.contains(variable)) {
95 kill.add(variable);
96 }
97 accesses.add(new VariableAccess.write(variable));
98 }
99
100 Block(this.catchBlock) {
101 if (catchBlock != null) {
102 catchBlock.catchPredecessors.add(this);
103 }
104 }
105 }
106
107 /// Builds a control-flow graph suitable for performing liveness analysis.
108 class BlockGraphBuilder extends RecursiveVisitor {
109 Map<Label, Block> _jumpTarget = <Label, Block>{};
110 Block _currentBlock;
111 List<Block> blocks = <Block>[];
112
113 /// Variables with an assignment that should be treated as final.
114 ///
115 /// Such variables cannot be merged with any other variables, so we exclude
116 /// them from the control-flow graph entirely.
117 Set<Variable> _ignoredVariables = new Set<Variable>();
118
119 BlockGraphBuilder() {
120 _currentBlock = newBlock();
121 }
122
123 /// Creates a new block with the current exception handler or [catchBlock]
124 /// if provided.
125 Block newBlock({Block catchBlock}) {
126 if (catchBlock == null && _currentBlock != null) {
127 catchBlock = _currentBlock.catchBlock;
128 }
129 Block block = new Block(catchBlock);
130 blocks.add(block);
131 return block;
132 }
133
134 /// Starts a new block after the end of [block].
135 void branchFrom(Block block, {Block catchBlock}) {
136 _currentBlock = newBlock(catchBlock: catchBlock)..predecessors.add(block);
137 }
138
139 /// Called when reading from [variable].
140 ///
141 /// Appends a read operation to the current basic block.
142 void read(Variable variable) {
143 if (variable.isCaptured) return;
144 if (_ignoredVariables.contains(variable)) return;
145 _currentBlock.addRead(variable);
146 }
147
148 /// Called when writing to [variable].
149 ///
150 /// Appends a write operation to the current basic block.
151 void write(Variable variable) {
152 if (variable.isCaptured) return;
153 if (_ignoredVariables.contains(variable)) return;
154 _currentBlock.addWrite(variable);
155 }
156
157 /// Called to indicate that [variable] should not be merged, and therefore
158 /// be excluded from the control-flow graph.
159 /// Subsequent calls to [read] and [write] will ignore it.
160 void ignoreVariable(Variable variable) {
161 _ignoredVariables.add(variable);
162 }
163
164 visitVariableUse(VariableUse node) {
165 read(node.variable);
166 }
167
168 visitAssign(Assign node) {
169 visitExpression(node.value);
170 write(node.variable);
171 visitStatement(node.next);
172 }
173
174 visitIf(If node) {
175 visitExpression(node.condition);
176 Block afterCondition = _currentBlock;
177 branchFrom(afterCondition);
178 visitStatement(node.thenStatement);
179 branchFrom(afterCondition);
180 visitStatement(node.elseStatement);
181 }
182
183 visitLabeledStatement(LabeledStatement node) {
184 Block join = _jumpTarget[node.label] = newBlock();
185 visitStatement(node.body); // visitBreak will add predecessors to join.
186 _currentBlock = join;
187 visitStatement(node.next);
188 }
189
190 visitBreak(Break node) {
191 _jumpTarget[node.target].predecessors.add(_currentBlock);
192 }
193
194 visitContinue(Continue node) {
195 _jumpTarget[node.target].predecessors.add(_currentBlock);
196 }
197
198 visitWhileTrue(WhileTrue node) {
199 Block join = _jumpTarget[node.label] = newBlock();
200 join.predecessors.add(_currentBlock);
201 _currentBlock = join;
202 visitStatement(node.body); // visitContinue will add predecessors to join.
203 }
204
205 visitWhileCondition(WhileCondition node) {
206 Block join = _jumpTarget[node.label] = newBlock();
207 join.predecessors.add(_currentBlock);
208 _currentBlock = join;
209 visitExpression(node.condition);
210 Block afterCondition = _currentBlock;
211 branchFrom(afterCondition);
212 visitStatement(node.body); // visitContinue will add predecessors to join.
213 branchFrom(afterCondition);
214 visitStatement(node.next);
215 }
216
217 visitTry(Try node) {
218 Block catchBlock = newBlock();
219 branchFrom(_currentBlock, catchBlock: catchBlock);
220 visitStatement(node.tryBody);
221 _currentBlock = catchBlock;
222 // Catch parameters cannot be hoisted to the top of the function, so to
223 // avoid complications with scoping, we do not attempt to merge them.
224 node.catchParameters.forEach(ignoreVariable);
225 visitStatement(node.catchBody);
226 }
227
228 visitConditional(Conditional node) {
229 visitExpression(node.condition);
230 // TODO(asgerf): When assignment expressions are added, this is no longer
231 // sound; then we need to handle as a branch.
232 visitExpression(node.thenExpression);
233 visitExpression(node.elseExpression);
234 }
235
236 visitLogicalOperator(LogicalOperator node) {
237 visitExpression(node.left);
238 // TODO(asgerf): When assignment expressions are added, this is no longer
239 // sound; then we need to handle as a branch.
240 visitExpression(node.right);
241 }
242
243 visitFunctionDeclaration(FunctionDeclaration node) {
244 // The function variable is final, hence cannot be merged.
245 ignoreVariable(node.variable);
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 }
268 }
269
270 /// Computes liveness information of the given control-flow graph.
271 ///
272 /// The results are stored in [Block.liveIn] and [Block.liveOut].
273 void _computeLiveness(List<Block> blocks) {
274 // 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
276 // is a good ordering.
277 // The choice of LIFO for re-inserted blocks is currently arbitrary,
278 List<Block> worklist = new List<Block>.from(blocks);
279 while (!worklist.isEmpty) {
280 Block block = worklist.removeLast();
281 block.inWorklist = false;
282
283 bool changed = false;
284
285 // The liveIn set is computed as:
286 //
287 // liveIn = (liveOut - kill) + gen
288 //
289 // We do the computation in two steps:
290 //
291 // 1. liveIn = gen
292 // 2. liveIn += (liveOut - kill)
293 //
294 // However, since liveIn only grows, and gen never changes, we only have
295 // to do the first step at the first iteration. Moreover, the gen set is
296 // not needed anywhere else, so we don't even need to copy it.
297 if (block.liveIn == null) {
298 block.liveIn = block.gen;
299 block.gen = null;
300 changed = true;
301 }
302
303 // liveIn += (liveOut - kill)
304 for (Variable variable in block.liveOut) {
305 if (!block.kill.contains(variable)) {
306 if (block.liveIn.add(variable)) {
307 changed = true;
308 }
309 }
310 }
311
312 // If anything changed, propagate liveness backwards.
313 if (changed) {
314 // Propagate live variables to predecessors.
315 for (Block predecessor in block.predecessors) {
316 int lengthBeforeChange = predecessor.liveOut.length;
317 predecessor.liveOut.addAll(block.liveIn);
318 if (!predecessor.inWorklist &&
319 predecessor.liveOut.length != lengthBeforeChange) {
320 worklist.add(predecessor);
321 predecessor.inWorklist = true;
322 }
323 }
324
325 // Propagate live variables to catch predecessors.
326 for (Block pred in block.catchPredecessors) {
327 bool changed = false;
328 int lengthBeforeChange = pred.liveOut.length;
329 pred.liveOut.addAll(block.liveIn);
330 if (pred.liveOut.length != lengthBeforeChange) {
331 changed = true;
332 }
333 // Assigning to a variable that is live in the catch block, does not
334 // kill the variable, because we conservatively assume that an exception
335 // could be thrown immediately before the assignment.
336 // Therefore remove live variables from all kill sets inside the try.
337 // Since the kill set is only used to subtract live variables from a
338 // set, the analysis remains monotone.
339 lengthBeforeChange = pred.kill.length;
340 pred.kill.removeAll(block.liveIn);
341 if (pred.kill.length != lengthBeforeChange) {
342 changed = true;
343 }
344 if (changed && !pred.inWorklist) {
345 worklist.add(pred);
346 pred.inWorklist = true;
347 }
348 }
349 }
350 }
351 }
352
353 /// For testing purposes, this flag can be passed to merge variables that
354 /// originated from different source variables.
355 ///
356 /// Correctness should not depend on the fact that we only merge variable
357 /// originating from the same source variable. Setting this flag makes a bug
358 /// more likely to provoke a test case failure.
359 const bool NO_PRESERVE_VARS = const bool.fromEnvironment('NO_PRESERVE_VARS');
360
361 /// Based on liveness information, computes a map of variable substitutions to
362 /// merge variables.
363 ///
364 /// Constructs a register interference graph. This is an undirected graph of
365 /// variables, with an edge between two variables if they cannot be merged
366 /// (because they are live simultaneously).
367 ///
368 /// We then compute a graph coloring, where the color of a node denotes which
369 /// variable it will be substituted by.
370 ///
371 /// We never merge variables that originated from distinct source variables,
372 /// so we build a separate register interference graph for each source variable.
373 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks) {
374 Map<Variable, Set<Variable>> interference = <Variable, Set<Variable>>{};
375
376 /// Group for the given variable. We attempt to merge variables in the same
377 /// group.
378 /// By default, variables are grouped based on their source variable, but
379 /// this can be disabled for testing purposes.
380 Local group(Variable variable) {
381 if (NO_PRESERVE_VARS) {
382 // Parameters may not occur more than once in a parameter list,
383 // so except for parameters, we try to merge all variables.
384 return variable.element is ParameterElement ? variable.element : null;
385 }
386 return variable.element;
387 }
388
389 Set<Variable> empty = new Set<Variable>();
390
391 // At the assignment to a variable x, add an edge to every variable that is
392 // live after the assignment (if it came from the same source variable).
393 for (Block block in blocks) {
394 // Group the liveOut set by source variable.
395 Map<Local, Set<Variable>> liveOut = <Local, Set<Variable>>{};
396 for (Variable variable in block.liveOut) {
397 liveOut.putIfAbsent(
398 group(variable),
399 () => new Set<Variable>()).add(variable);
400 interference.putIfAbsent(variable, () => new Set<Variable>());
401 }
402 // Get variables that are live at the catch block.
403 Set<Variable> liveCatch = block.catchBlock != null
404 ? block.catchBlock.liveIn
405 : empty;
406 // Add edges for each variable being assigned here.
407 for (VariableAccess access in block.accesses.reversed) {
408 Variable variable = access.variable;
409 interference.putIfAbsent(variable, () => new Set<Variable>());
410 Set<Variable> live =
411 liveOut.putIfAbsent(group(variable), () => new Set<Variable>());
412 if (access.isRead) {
413 live.add(variable);
414 } else {
415 if (!liveCatch.contains(variable)) {
416 // Assignment to a variable that is not live in the catch block.
417 live.remove(variable);
418 }
419 for (Variable other in live) {
420 interference[variable].add(other);
421 interference[other].add(variable);
422 }
423 }
424 }
425 }
426
427 // Sort the variables by descending degree.
428 // The most constrained variables will be assigned a color first.
429 List<Variable> variables = interference.keys.toList();
430 variables.sort((x, y) => interference[y].length - interference[x].length);
431
432 Map<Local, List<Variable>> registers = <Local, List<Variable>>{};
433 Map<Variable, Variable> subst = <Variable, Variable>{};
434
435 for (Variable v1 in variables) {
436 List<Variable> register = registers[group(v1)];
437
438 // Optimization: For the first variable in a group, allocate a new color
439 // without iterating over its interference edges.
440 if (register == null) {
441 registers[group(v1)] = <Variable>[v1];
442 subst[v1] = v1;
443 continue;
444 }
445
446 // Optimization: If there are no inteference edges for this variable,
447 // assign it the first color without copying the register list.
448 Set<Variable> interferenceSet = interference[v1];
449 if (interferenceSet.isEmpty) {
450 subst[v1] = register[0];
451 continue;
452 }
453
454 // Find an unused color.
455 Set<Variable> potential = new Set<Variable>.from(register);
456 for (Variable v2 in interferenceSet) {
457 Variable v2subst = subst[v2];
458 if (v2subst != null) {
459 potential.remove(v2subst);
460 if (potential.isEmpty) break;
461 }
462 }
463
464 if (potential.isEmpty) {
465 // If no free color was found, add this variable as a new color.
466 register.add(v1);
467 subst[v1] = v1;
468 } else {
469 subst[v1] = potential.first;
470 }
471 }
472
473 return subst;
474 }
475
476 /// Performs variable substitution and removes redundant assignments.
477 class SubstituteVariables extends RecursiveVisitor {
478
479 Map<Variable, Variable> mapping;
480
481 SubstituteVariables(this.mapping);
482
483 Variable replaceRead(Variable variable) {
484 Variable w = mapping[variable];
485 if (w == null) return variable; // Skip ignored variables.
486 w.readCount++;
487 variable.readCount--;
488 return w;
489 }
490
491 Variable replaceWrite(Variable variable) {
492 Variable w = mapping[variable];
493 if (w == null) return variable; // Skip ignored variables.
494 w.writeCount++;
495 variable.writeCount--;
496 return w;
497 }
498
499 void replaceParameters(List<Variable> parameters) {
500 for (int i = 0; i < parameters.length; ++i) {
501 parameters[i] = replaceWrite(parameters[i]);
502 }
503 }
504
505 visitVariableUse(VariableUse node) {
506 node.variable = replaceRead(node.variable);
507 }
508
509 visitFunctionDefinition(FunctionDefinition node) {
510 replaceParameters(node.parameters);
511 node.body = visitStatement(node.body);
512 }
513
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) {
535 node.variable = replaceWrite(node.variable);
536
537 visitExpression(node.value);
538 node.next = visitStatement(node.next);
539
540 // Remove assignments of form "x := x"
541 if (node.value is VariableUse) {
542 VariableUse value = node.value;
543 if (value.variable == node.variable) {
544 value.variable.readCount--;
545 node.variable.writeCount--;
546 return node.next;
547 }
548 }
549
550 return node;
551 }
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 }
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