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

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: Created 5 years, 9 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 library tree_ir.optimization.variable_merger;
2
3 import 'optimization.dart' show Pass, PassMixin;
4 import '../tree_ir_nodes.dart';
5 import '../../elements/elements.dart' show Local;
6
7 /// Merges variables based on liveness and source variable information.
8 ///
9 /// This phase cleans up artifacts introduced by the translation through CPS,
10 /// where each source variable is translated into several copies. The copies
11 /// are merged again when they are not live simultaneously.
12 class VariableMerger extends RecursiveVisitor with PassMixin {
13 String get passName => 'Variable merger';
14
15 @override
16 void rewriteExecutableDefinition(ExecutableDefinition node) {
17 visitExecutableDefinition(node);
18 }
19
20 /// Rewrites the given function.
21 /// This is called for the outermost function and inner functions.
22 void rewriteFunction(ExecutableDefinition node) {
23 BlockGraphBuilder builder = new BlockGraphBuilder();
24 builder.visitExecutableDefinition(node);
25 _computeLiveness(builder.blocks);
26 Map<Variable, Variable> subst = _computeRegisterAllocation(builder.blocks);
27 new SubstVariables(subst).visitExecutableDefinition(node);
28 }
29
30 visitFunctionDefinition(FunctionDefinition node) {
31 super.visitFunctionDefinition(node); // Recurse to visit inner functions.
32 rewriteFunction(node);
33 }
34
35 visitFieldDefinition(FieldDefinition node) {
36 super.visitFieldDefinition(node);
37 rewriteFunction(node);
38 }
39
40 visitConstructorDefinition(ConstructorDefinition node) {
41 super.visitConstructorDefinition(node);
42 rewriteFunction(node);
43 }
44 }
45
46 /// Basic block in a control-flow graph.
47 ///
48 /// Each block consists of a sequence of reads or a sequence of writes.
Kevin Millikin (Google) 2015/03/26 14:35:54 Flesh out this comment a bit to say explicitly tha
asgerf 2015/03/27 15:18:26 Changed to basic blocks with interleaved operation
49 class Block {
50 /// List of predecessors in the control-flow graph.s
Kevin Millikin (Google) 2015/03/26 14:35:53 There's an extra 's'.
asgerf 2015/03/27 15:18:27 Done.
51 final List<Block> predecessors = <Block>[];
52
53 /// Entry to the catch block for the enclosing try, or `null`.
54 final Block catchBlock;
55
56 /// List of nodes with this block as [catchBlock].
57 final List<Block> catchPredecessors = <Block>[];
58
59 /// True if this is a sequence of read operations, false if write operations.
60 bool isRead = true;
61 bool get isWrite => !isRead;
62
63 /// Variables being read or written in this block.
Kevin Millikin (Google) 2015/03/26 14:35:53 I'd mention either "Sequence of variables" or "in
asgerf 2015/03/27 15:18:27 Done.
64 final List<Variable> variables = <Variable>[];
65
66 /// Auxilliary fields used by the liveness analysis.
Kevin Millikin (Google) 2015/03/26 14:35:54 One 'l' in auxiliary.
asgerf 2015/03/27 15:18:27 Done.
67 bool inWorklist = true;
68 Set<Variable> liveBefore = new Set<Variable>();
Kevin Millikin (Google) 2015/03/26 14:35:54 Consider liveIn and liveOut, which are fairly stan
asgerf 2015/03/27 15:18:26 Done.
69 Set<Variable> liveAfter = new Set<Variable>();
70
71 Block(this.catchBlock) {
72 if (catchBlock != null) {
73 catchBlock.catchPredecessors.add(this);
74 }
75 }
76 }
77
78 /// Builds a control-flow graph suitable for performing liveness analysis.
79 class BlockGraphBuilder extends RecursiveVisitor {
Kevin Millikin (Google) 2015/03/26 14:35:55 The blank line after class isn't necessary.
asgerf 2015/03/27 15:18:26 Done.
80
81 Map<Label, Block> jumpTarget = <Label, Block>{};
Kevin Millikin (Google) 2015/03/26 14:35:53 I'd make most of these private names, except for t
asgerf 2015/03/27 15:18:27 Done.
82 Block currentBlock;
83 List<Block> blocks = <Block>[];
84
85 /// Variables with an assignment that should be treated as final.
86 ///
87 /// Such variables cannot be merged with any other variables, so we exclude
88 /// them from the control-flow graph entirely.
89 Set<Variable> finalVariables = new Set<Variable>();
90
91 BlockGraphBuilder() {
92 currentBlock = newBlock();
93 }
94
95 /// Creates a new block with the current exception handler or [catchBlock]
96 /// if provided.
97 Block newBlock({Block catchBlock}) {
98 if (catchBlock == null && currentBlock != null) {
99 catchBlock = currentBlock.catchBlock;
100 }
101 Block block = new Block(catchBlock);
102 blocks.add(block);
103 return block;
104 }
105
106 /// Starts a new branch after the end of [block].
Kevin Millikin (Google) 2015/03/26 14:35:53 Not necessary a branch (at least, not a non-trivia
asgerf 2015/03/27 15:18:28 Done.
107 void branchFrom(Block block) {
108 currentBlock = newBlock()..predecessors.add(block);
109 }
110
111 /// Called when reading from [v].
112 ///
113 /// Appends a read operation to the current basic block, or starts a new
114 /// block if the current block is a write block.
115 void read(Variable v) {
Kevin Millikin (Google) 2015/03/26 14:35:54 Go ahead and spell out 'variable', here and below.
asgerf 2015/03/27 15:18:27 Done.
116 if (v.isCaptured) return;
117 if (finalVariables.contains(v)) return;
118 if (!currentBlock.isRead) {
119 branchFrom(currentBlock);
120 currentBlock.isRead = true;
Kevin Millikin (Google) 2015/03/26 14:35:53 true is the default and we do rely on that a bit b
asgerf 2015/03/27 15:18:26 Not relevant anymore.
121 }
122 currentBlock.variables.add(v);
123 }
124
125 /// Called when writing to [v].
126 ///
127 /// Appends a write operation to the current basic block, or starts a new
128 /// block if the current block is a read block.
129 void write(Variable v) {
130 if (v.isCaptured) return;
131 if (finalVariables.contains(v)) return;
132 if (currentBlock.isRead) {
133 if (!currentBlock.variables.isEmpty) {
Kevin Millikin (Google) 2015/03/26 14:35:54 This needs a short comment to explain that this co
asgerf 2015/03/27 15:18:27 Not relevant anymore.
134 branchFrom(currentBlock);
135 }
136 currentBlock.isRead = false;
137 }
138 currentBlock.variables.add(v);
139 }
140
141 /// Called to indicate that [v] has a final assignment, and should therefore
142 /// be ignored. Subsequent calls to [read] and [write] will ignore the it.
143 void finalWrite(Variable v) {
144 finalVariables.add(v);
145 }
146
147 visitVariableUse(VariableUse node) {
148 read(node.variable);
149 }
150
151 visitAssign(Assign node) {
152 visitExpression(node.value);
153 write(node.variable);
154 visitStatement(node.next);
155 }
156
157 visitIf(If node) {
158 visitExpression(node.condition);
159 Block afterCondition = currentBlock;
160 branchFrom(afterCondition);
161 visitStatement(node.thenStatement);
162 branchFrom(afterCondition);
163 visitStatement(node.elseStatement);
164 }
165
166 visitLabeledStatement(LabeledStatement node) {
167 Block join = jumpTarget[node.label] = newBlock();
168 visitStatement(node.body); // visitBreak will add predecessors to join.
169 currentBlock = join;
170 visitStatement(node.next);
171 }
172
173 visitBreak(Break node) {
174 jumpTarget[node.target].predecessors.add(currentBlock);
175 }
176
177 visitContinue(Continue node) {
178 jumpTarget[node.target].predecessors.add(currentBlock);
179 }
180
181 visitWhileTrue(WhileTrue node) {
182 Block join = jumpTarget[node.label] = newBlock();
183 join.predecessors.add(currentBlock);
184 currentBlock = join;
185 visitStatement(node.body); // visitContinue will add predecessors to join.
186 }
187
188 visitWhileCondition(WhileCondition node) {
189 Block join = jumpTarget[node.label] = newBlock();
190 join.predecessors.add(currentBlock);
191 currentBlock = join;
192 visitExpression(node.condition);
193 Block afterCondition = currentBlock;
194 branchFrom(afterCondition);
195 visitStatement(node.body); // visitContinue will add predecessors to join.
196 branchFrom(afterCondition);
197 visitStatement(node.next);
198 }
199
200 visitTry(Try node) {
201 Block catchBlock = newBlock();
202 Block tryBlock = newBlock(catchBlock: catchBlock);
Kevin Millikin (Google) 2015/03/26 14:35:53 The three lines starting here are branchFrom with
asgerf 2015/03/27 15:18:27 Done.
203 tryBlock.predecessors.add(currentBlock);
204 currentBlock = tryBlock;
205 visitStatement(node.tryBody);
206 currentBlock = catchBlock;
207 node.catchParameters.forEach(finalWrite);
Kevin Millikin (Google) 2015/03/26 14:35:53 Is this necessary or defensive? It seems like sin
asgerf 2015/03/27 15:18:27 Both. Something smarter could be done, but it's no
208 visitStatement(node.catchBody);
209 }
210
211 visitConditional(Conditional node) {
212 visitExpression(node.condition);
213 // TODO(asgerf): When assignment expressions are added, this is no longer
214 // sound; then we need to handle as a branch.
215 visitExpression(node.thenExpression);
216 visitExpression(node.elseExpression);
217 }
218
219 visitLogicalOperator(LogicalOperator node) {
220 visitExpression(node.left);
221 // TODO(asgerf): When assignment expressions are added, this is no longer
222 // sound; then we need to handle as a branch.
223 visitExpression(node.right);
224 }
225
226 visitFunctionDeclaration(FunctionDeclaration node) {
227 finalWrite(node.variable);
228 visitStatement(node.next);
229 // Do not traverse inner function.
230 }
231
232 visitFunctionExpression(FunctionExpression node) {
233 // Do not traverse inner function.
234 }
235
236 visitFunctionDefinition(FunctionDefinition node) {
237 // Function parameters are treated as write operations at the entry point,
238 // so they can potentially be merged with other copies of the parameter.
239 // Note that function parameters always have distinct source variables,
240 // so we don't risk accidentally merging two parameters.
241 node.parameters.forEach(write);
242 visitStatement(node.body);
243 }
244
245 visitConstructorDefinition(ConstructorDefinition node) {
246 node.parameters.forEach(write);
247 node.initializers.forEach(visitInitializer);
248 visitStatement(node.body);
249 }
250 }
251
252 /// Computes liveness information of the given control-flow graph.
253 ///
254 /// The results are stored in [Block.liveBefore] and [Block.liveAfter].
255 void _computeLiveness(List<Block> blocks) {
256 List<Block> worklist = new List<Block>.from(blocks);
Kevin Millikin (Google) 2015/03/26 14:35:53 Comment that blocks are initially in AST order and
asgerf 2015/03/27 15:18:26 Done.
257 while (!worklist.isEmpty) {
258 Block block = worklist.removeLast();
259 block.inWorklist = false;
260 Set<Variable> live = new Set<Variable>.from(block.liveAfter);
Kevin Millikin (Google) 2015/03/26 14:35:53 This is potentially expensive, it's making an iter
asgerf 2015/03/27 15:18:27 Done.
261 if (block.isRead) {
262 // Reading a variable makes it live before that point.
263 live.addAll(block.variables);
Kevin Millikin (Google) 2015/03/26 14:35:54 I think it is worth pointing out that our blocks a
asgerf 2015/03/27 15:18:28 Not relevant anymore.
264 } else {
265 // Assigning to a variable makes it dead before that point.
266 // Note that when a variable is live at entry to the current catch block,
267 // it remains live before the assignment.
268 // When a variable becomes live at the catch block, it will be removed
269 // from block.variables, so here we can safely mark them all as dead.
270 live.removeAll(block.variables);
271 }
272
273 // If anything changed, propagate liveness backwards.
274 if (block.liveBefore.length < live.length) {
Kevin Millikin (Google) 2015/03/26 14:35:54 Comment that this is a monotone analysis: the live
asgerf 2015/03/27 15:18:27 I agree, but there is now an explicit changed flag
275 block.liveBefore = live;
276
277 // Propagate live variables to predecessors.
278 for (Block pred in block.predecessors) {
Kevin Millikin (Google) 2015/03/26 14:35:53 Spell out predecessor.
asgerf 2015/03/27 15:18:27 Done.
279 int size = pred.liveAfter.length;
Kevin Millikin (Google) 2015/03/26 14:35:54 size ==> length, or originalLength or the like.
asgerf 2015/03/27 15:18:28 Done.
280 pred.liveAfter.addAll(live);
281 if (pred.liveAfter.length > size && !pred.inWorklist) {
282 worklist.add(pred);
283 pred.inWorklist = true;
284 }
285 }
286
287 // Propagate live variables to catch predecessors.
288 for (Block pred in block.catchPredecessors) {
289 bool changed = false;
290 int size = pred.liveAfter.length;
Kevin Millikin (Google) 2015/03/26 14:35:54 Use 'length' in the name, not 'size'.
asgerf 2015/03/27 15:18:27 Done.
291 pred.liveAfter.addAll(live);
292 if (size < pred.liveAfter.length) {
Kevin Millikin (Google) 2015/03/26 14:35:54 The analogous comparison in the loop above has siz
asgerf 2015/03/31 12:14:18 Done.
293 changed = true;
294 }
295 if (pred.isWrite) {
296 // Remove assignments to variables that are live in the catch block.
297 size = pred.variables.length;
298 pred.variables.removeWhere(block.liveBefore.contains);
Kevin Millikin (Google) 2015/03/26 14:35:55 block.liveBefore is the same as live, isn't it? I
asgerf 2015/03/27 15:18:27 Done.
299 if (pred.variables.length < size) {
300 changed = true;
301 }
302 }
303 if (!pred.inWorklist && changed) {
Kevin Millikin (Google) 2015/03/26 14:35:54 Probably cheaper to check changed before !pred.inW
asgerf 2015/03/27 15:18:28 Done.
304 worklist.add(pred);
305 pred.inWorklist = true;
306 }
307 }
308 }
309 }
310 }
311
312 /// Based on liveness information, computes a map of variable substitutions to
313 /// merge variables.
314 ///
315 /// Constructs a register interference graph. This is an undirected graph of
316 /// variables, with an edge between two variables if they cannot be merged
317 /// (because they are live simultaneously).
318 ///
319 /// We then compute a graph coloring, where the color of a node denotes which
320 /// variable it will be substituted by.
321 ///
322 /// We never merge variables that originated from distinct source variables,
323 /// so we build a separate register interference graph for each source variable.
324 Map<Variable, Variable> _computeRegisterAllocation(List<Block> blocks) {
325 Map<Variable, Set<Variable>> edges = new Map<Variable, Set<Variable>>();
Kevin Millikin (Google) 2015/03/26 14:35:55 edges ==> interferences
asgerf 2015/03/27 15:18:28 Done.
326
327 // At the assignment to a variable x, add an edge to every variable that is
328 // live after the assignment (if it came from the same source variable).
329 for (Block block in blocks) {
330 if (block.isWrite) {
331 // Group the liveAfter set by source variable.
332 Map<Local, List<Variable>> liveAfter = <Local, List<Variable>>{};
333 for (Variable x in block.liveAfter) {
Kevin Millikin (Google) 2015/03/26 14:35:53 x ==> variable
asgerf 2015/03/27 15:18:27 Done.
334 liveAfter.putIfAbsent(x.element, () => <Variable>[]).add(x);
335 edges.putIfAbsent(x, () => new Set<Variable>());
336 }
337 // Add edges for each variable being assigned here.
338 for (Variable x in block.variables.reversed) {
339 edges.putIfAbsent(x, () => new Set<Variable>());
340 List<Variable> live = liveAfter[x.element];
341 if (live != null) {
342 live.remove(x); // Hide from earlier assignments in the block.
343 for (Variable y in live) {
344 edges[x].add(y);
345 edges[y].add(x);
346 }
347 }
348 }
349 }
350 }
351
352 // Sort the variables by descending degree.
353 // The most constrained variables will be assigned a color first.
354 List<Variable> variables = edges.keys.toList();
355 variables.sort((x, y) => edges[y].length - edges[x].length);
356
357 Map<Local, List<Variable>> registers = <Local, List<Variable>>{};
358 Map<Variable, Variable> subst = <Variable, Variable>{};
359
360 for (Variable v1 in variables) {
361 List<Variable> register =
362 registers.putIfAbsent(v1.element, () => <Variable>[]);
363
364 // Find an unused color.
365 Set<Variable> potential = new Set<Variable>.from(register);
366 for (Variable v2 in edges[v1]) {
Kevin Millikin (Google) 2015/03/26 14:35:54 I don't have a feel for how big the sets of interf
asgerf 2015/03/27 15:18:27 When compiling swarm, it's empty about 90% of the
367 Variable v2subst = subst[v2];
368 if (v2subst != null) {
369 potential.remove(v2subst);
370 }
371 }
372 if (potential.isEmpty) {
373 // If no free color was found, add this variable as a new color.
374 register.add(v1);
375 subst[v1] = v1;
376 } else {
377 subst[v1] = potential.first;
378 }
379 }
380
381 return subst;
382 }
383
384 /// Performs variable substitution and removes redundant assignments.
385 class SubstVariables extends RecursiveVisitor {
Kevin Millikin (Google) 2015/03/26 14:35:53 I'm not sure what Subst is supposed to be. The ve
asgerf 2015/03/27 15:18:26 Done.
386
387 Map<Variable, Variable> subst;
388
389 SubstVariables(this.subst);
390
391 Variable replaceRead(Variable v) {
Kevin Millikin (Google) 2015/03/26 14:35:54 v ==> variable, w ==> other.
asgerf 2015/03/27 15:18:27 Done.
392 Variable w = subst[v];
393 if (w == null) return v;
Kevin Millikin (Google) 2015/03/26 14:35:54 This is the case for final assignments, or does it
asgerf 2015/03/27 15:18:26 Done.
394 w.readCount++;
395 v.readCount--;
396 return w;
397 }
398
399 Variable replaceWrite(Variable v) {
400 Variable w = subst[v];
401 if (w == null) return v;
402 w.writeCount++;
403 v.writeCount--;
404 return w;
405 }
406
407 void replaceParameters(List<Variable> parameters) {
408 for (int i=0; i < parameters.length; i++) {
Kevin Millikin (Google) 2015/03/26 14:35:54 'i = 0'.
asgerf 2015/03/27 15:18:27 Done.
409 parameters[i] = replaceWrite(parameters[i]);
410 }
411 }
412
413 visitVariableUse(VariableUse node) {
414 node.variable = replaceRead(node.variable);
415 }
416
417 visitFunctionDefinition(FunctionDefinition node) {
418 replaceParameters(node.parameters);
419 node.body = visitStatement(node.body);
420 }
421
422 visitConstructorDefinition(ConstructorDefinition node) {
423 replaceParameters(node.parameters);
424 node.initializers.forEach(visitInitializer);
425 node.body = visitStatement(node.body);
426 }
427
428 visitFieldInitializer(FieldInitializer node) {
429 node.body = visitStatement(node.body);
430 }
431
432 visitSuperInitializer(SuperInitializer node) {
433 for (int i=0; i<node.arguments.length; i++) {
Kevin Millikin (Google) 2015/03/26 14:35:54 'i = 0', 'i < node.arguments.length'. And '++i' :
asgerf 2015/03/27 15:18:26 Done.
434 node.arguments[i] = visitStatement(node.arguments[i]);
435 }
436 }
437
438 // Statement visitors should return the transformed statement so we
439 // can remove redundant assignments.
440 Statement visitStatement(Statement node) => super.visitStatement(node);
441
442 Statement visitAssign(Assign node) {
443 node.variable = replaceWrite(node.variable);
444
445 visitExpression(node.value);
446 node.next = visitStatement(node.next);
447
448 // Remove assignments of form "x := x"
449 if (node.value is VariableUse) {
450 VariableUse value = node.value;
451 if (value.variable == node.variable) {
452 value.variable.readCount--;
453 node.variable.writeCount--;
454 return node.next;
455 }
456 }
457
458 return node;
459 }
460
461 Statement visitLabeledStatement(LabeledStatement node) {
462 node.body = visitStatement(node.body);
463 node.next = visitStatement(node.next);
464 return node;
465 }
466
467 Statement visitReturn(Return node) {
468 visitExpression(node.value);
469 return node;
470 }
471
472 Statement visitBreak(Break node) => node;
Kevin Millikin (Google) 2015/03/26 14:35:54 I usually make all the 'related' method bodies hav
asgerf 2015/03/27 15:18:27 Done.
473
474 Statement visitContinue(Continue node) => node;
475
476 Statement visitIf(If node) {
477 visitExpression(node.condition);
478 node.thenStatement = visitStatement(node.thenStatement);
479 node.elseStatement = visitStatement(node.elseStatement);
480 return node;
481 }
482
483 Statement visitWhileTrue(WhileTrue node) {
484 node.body = visitStatement(node.body);
485 return node;
486 }
487
488 Statement visitWhileCondition(WhileCondition node) {
489 visitExpression(node.condition);
490 node.body = visitStatement(node.body);
491 node.next = visitStatement(node.next);
492 return node;
493 }
494
495 Statement visitFunctionDeclaration(FunctionDeclaration node) {
496 node.next = visitStatement(node.next);
497 return node;
498 }
499
500 Statement visitExpressionStatement(ExpressionStatement node) {
501 visitExpression(node.expression);
502 node.next = visitStatement(node.next);
503 return node;
504 }
505
506 Statement visitTry(Try node) {
507 node.tryBody = visitStatement(node.tryBody);
508 node.catchBody = visitStatement(node.catchBody);
509 return node;
510 }
511
512 Statement visitSetField(SetField node) {
513 visitExpression(node.object);
514 visitExpression(node.value);
515 node.next = visitStatement(node.next);
516 return node;
517 }
518 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698