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

Side by Side Diff: sdk/lib/_internal/compiler/implementation/cps_ir/shrinking_reductions.dart

Issue 417043003: Implement shrinking reductions in CPS IR (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Remove unused method Created 6 years, 4 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) 2014, 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 part of dart2js.cps_optimizers;
6
7 /**
8 * [[ShrinkingReducer]] applies shrinking reductions to CPS terms as described
9 * in 'Compiling with Continuations, Continued' by Andrew Kennedy.
10 */
11 class ShrinkingReducer implements Pass {
12 _RedexVisitor _redexVisitor;
13 Set<_ReductionTask> _worklist;
14
15 static final _DeletedNode _DELETED = new _DeletedNode();
16
17 /// Applies shrinking reductions to root, mutating root in the process.
18 void rewrite(FunctionDefinition root) {
19 _worklist = new Set<_ReductionTask>();
20 _redexVisitor = new _RedexVisitor(_worklist);
21
22 // Set all parent pointers.
23 new _ParentVisitor().visit(root);
24
25 // Sweep over the term, collecting redexes into the worklist.
26 _redexVisitor.visitFunctionDefinition(root);
27
28 // Process the worklist.
29 while (_worklist.isNotEmpty) {
30 _ReductionTask task = _worklist.first;
31 _worklist.remove(task);
32 _processTask(task);
33 }
34 }
35
36 void _processTask(_ReductionTask task) {
37 // Lazily skip tasks for deleted nodes.
38 if (task.node.parent == _DELETED) {
39 return;
40 }
41
42 switch (task.kind) {
43 case _ReductionKind.DEAD_VAL:
44 _reduceDeadVal(task);
45 break;
46 case _ReductionKind.DEAD_CONT:
47 _reduceDeadCont(task);
48 break;
49 case _ReductionKind.BETA_CONT_LIN:
50 _reduceBetaContLin(task);
51 break;
52 case _ReductionKind.ETA_CONT:
53 _reduceEtaCont(task);
54 break;
55 default:
56 assert(false);
57 }
58 }
59
60 /// Applies the dead-val reduction:
61 /// letprim x = V in K -> K (x not free in K).
62 void _reduceDeadVal(_ReductionTask task) {
63 if (!_redexVisitor.isDeadVal(task.node)) {
sigurdm 2014/08/01 09:09:41 Add comment why this can happen
jgruber1 2014/08/07 09:07:36 Done, and replaced by assert in dead-* reductions.
64 return;
65 }
66
67 LetPrim letPrim = task.node;
68 NodeWithBody parent = letPrim.parent;
69 assert(letPrim != null && parent != null);
70
71 assert(!letPrim.primitive.hasAtLeastOneUse);
72
73 // Remove dead primitive.
74 letPrim.body.parent = parent;
75 parent.body = letPrim.body;
76
77 // Perform bookkeeping on removed body and scan for new redexes.
78 new _RemovalRedexVisitor(_worklist).visit(letPrim.primitive);
79 }
80
81 /// Applies the dead-cont reduction:
82 /// letcont k x = L in K -> K (k not free in K).
83 void _reduceDeadCont(_ReductionTask task) {
84 if (!_redexVisitor.isDeadCont(task.node)) {
85 return;
86 }
87
88 LetCont letCont = task.node;
89 NodeWithBody parent = letCont.parent;
90 assert(letCont != null && parent != null);
91
92 assert(!letCont.continuation.hasAtLeastOneUse);
93
94 // Remove dead continuation.
95 letCont.body.parent = parent;
96 parent.body = letCont.body;
97
98 // Perform bookkeeping on removed body and scan for new redexes.
99 new _RemovalRedexVisitor(_worklist).visit(letCont.continuation);
100 }
101
102 /// Applies the beta-cont-lin reduction:
103 /// letcont k x = K in C[k y] -> C[K[y/x]] (k not free in C).
104 void _reduceBetaContLin(_ReductionTask task) {
105 if (!_redexVisitor.isBetaContLin(task.node)) {
106 return;
107 }
108
109 LetCont letCont = task.node;
110 Continuation cont = letCont.continuation;
111 NodeWithBody parent = letCont.parent;
112 assert(letCont != null && parent != null);
113
114 assert(cont.hasExactlyOneUse);
115
116 // Remove the continuation.
117 letCont.body.parent = parent;
sigurdm 2014/08/01 09:09:41 You might be able to factor these two lines out.
jgruber1 2014/08/07 09:07:36 Done.
118 parent.body = letCont.body;
119
120 // Replace its invocation with the continuation body.
121 Reference ref = cont.firstRef..unlink();
122 InvokeContinuation invoke = ref.parent;
123 NodeWithBody invokeParent = invoke.parent;
124 assert(invoke != null && invokeParent != null);
125
126 cont.body.parent = invokeParent;
127 invokeParent.body = cont.body;
128
129 // Substitute the invocation argument for the continuation parameter.
130 for (int i = 0; i < invoke.arguments.length; i++) {
131 Reference argRef = invoke.arguments[i]..unlink();
132 argRef.definition.substituteFor(cont.parameters[i]);
133 // Scan for new redexes in substituted references.
134 _redexVisitor.processReference(argRef);
135 }
136
137 // Do not scan for new redexes in the continuation body to avoid quadratic
138 // blowup.
139 }
140
141 /// Applies the eta-cont reduction:
142 /// letcont k x = j x in K -> K[j/k].
143 void _reduceEtaCont(_ReductionTask task) {
144 if (!_redexVisitor.isEtaCont(task.node)) {
145 return;
146 }
147
148 LetCont letCont = task.node;
149 Continuation cont = letCont.continuation;
150 NodeWithBody parent = letCont.parent;
151 assert(letCont != null && parent != null);
152
153 assert(cont.hasAtLeastOneUse);
154
155 // Remove the continuation.
156 letCont.body.parent = parent;
157 parent.body = letCont.body;
158
159 InvokeContinuation invoke = cont.body;
160 Continuation wrappedCont = invoke.continuation.definition;
161
162 // Replace all occurrences with the wrapped continuation.
163 wrappedCont.substituteFor(cont);
164 _redexVisitor.processReference(invoke.continuation);
165
166 // Do not scan for new redexes in the letcont body to avoid quadratic
167 // blowup.
168 }
169 }
170
171 /// Traverses a term and adds any found redexes to the worklist.
172 class _RedexVisitor extends RecursiveVisitor {
173 final Set<_ReductionTask> worklist;
174
175 _RedexVisitor(this.worklist);
176
177 processLetPrim(LetPrim node) {
178 if (isDeadVal(node)) {
179 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node));
180 }
181 }
182
183 processLetCont(LetCont node) {
184 if (isDeadCont(node)) {
185 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node));
186 }
187 if (isBetaContLin(node)){
188 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node));
189 }
190 if (isEtaCont(node)) {
191 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node));
192 }
193 }
194
195 processReference(Reference reference) {
196 if (reference.definition is Primitive) {
197 Primitive primitive = reference.definition;
198 Node parent = primitive.parent;
199 if (parent is LetPrim && isDeadVal(parent)) {
200 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent));
201 }
202 } else if (reference.definition is Continuation) {
203 Continuation continuation = reference.definition;
204 Node parent = continuation.parent;
205 if (parent is LetCont && isDeadCont(parent)) {
206 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, parent));
207 }
208 }
209 }
210
211 bool isDeadVal(LetPrim node) => !node.primitive.hasAtLeastOneUse;
212
213 bool isDeadCont(LetCont node) => !node.continuation.hasAtLeastOneUse;
214
215 bool isBetaContLin(LetCont node) {
216 Continuation cont = node.continuation;
217 if (!cont.hasExactlyOneUse) {
218 return false;
219 }
220
221 if (!(cont.firstRef.parent is InvokeContinuation)) {
222 return false;
223 }
224
225 InvokeContinuation invoke = cont.firstRef.parent;
226 return (cont == invoke.continuation.definition);
227 }
228
229 bool isEtaCont(LetCont node) {
230 Continuation cont = node.continuation;
231 if (!(cont.body is InvokeContinuation)) {
232 return false;
233 }
234
235 // Special case for continuations passed into one of { InvokeConstructor,
236 // InvokeMethod, InvokeStatic, ConcatenateStrings }, since
237 // these require a continuation that is used exactly once.
238 if (cont.hasExactlyOneUse) {
239 if (cont.firstRef.parent is InvokeConstructor) {
240 InvokeConstructor parent = cont.firstRef.parent;
241 if (parent.continuation == cont.firstRef) {
242 return false;
243 }
244 } else if (cont.firstRef.parent is InvokeMethod) {
245 InvokeMethod parent = cont.firstRef.parent;
246 if (parent.continuation == cont.firstRef) {
247 return false;
248 }
249 } else if (cont.firstRef.parent is InvokeStatic) {
250 InvokeStatic parent = cont.firstRef.parent;
251 if (parent.continuation == cont.firstRef) {
252 return false;
253 }
254 } else if (cont.firstRef.parent is ConcatenateStrings) {
255 ConcatenateStrings parent = cont.firstRef.parent;
256 if (parent.continuation == cont.firstRef) {
257 return false;
258 }
259 }
260 }
261
262 InvokeContinuation invoke = cont.body;
263 if (invoke.isRecursive) {
264 return false;
265 }
266
267 if (cont.parameters.length != invoke.arguments.length) {
268 return false;
269 }
270
271 // TODO(jgruber): Linear in the parameter count. Can be improved to near
272 // constant time by using union-find data structure.
273 for (int i = 0; i < cont.parameters.length; i++) {
274 if (invoke.arguments[i].definition != cont.parameters[i]) {
275 return false;
276 }
277 }
278
279 return true;
280 }
281 }
282
283 /// Traverses a deleted CPS term, marking existing tasks associated with a node
284 /// within the term as deleted (which causes them to be skipped lazily when
285 /// popped from the worklist), and adding newly created redexes to the worklist.
286 class _RemovalRedexVisitor extends _RedexVisitor {
287 _RemovalRedexVisitor(Set<_ReductionTask> worklist) : super(worklist);
288
289 processLetPrim(LetPrim node) {
290 node.parent = ShrinkingReducer._DELETED;
291 }
292
293 processLetCont(LetCont node) {
294 node.parent = ShrinkingReducer._DELETED;
295 }
296
297 processReference(Reference reference) {
298 reference.unlink();
299 super.processReference(reference); // Scan for new dead-* redexes.
300 }
301 }
302
303 /// Traverses the CPS term and sets node.parent for each visited node.
304 class _ParentVisitor extends RecursiveVisitor {
305
306 void setParent(Node parent, Node child) {
307 assert(child.parent == null);
308 child.parent = parent;
309 }
310
311 void setRefParent(Node parent, Reference child) {
312 assert(child.parent == null);
313 child.parent = parent;
314 }
315
316 processFunctionDefinition(FunctionDefinition node) {
317 setParent(node, node.body);
318 node.parameters.forEach((Parameter p) => setParent(node, p));
319 }
320
321 // Expressions.
322
323 processLetPrim(LetPrim node) {
324 setParent(node, node.primitive);
325 setParent(node, node.body);
326 }
327
328 processLetCont(LetCont node) {
329 setParent(node, node.continuation);
330 setParent(node, node.body);
331 }
332
333 processInvokeStatic(InvokeStatic node) {
334 setRefParent(node, node.continuation);
335 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
336 }
337
338 processInvokeContinuation(InvokeContinuation node) {
339 setRefParent(node, node.continuation);
340 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
341 }
342
343 processInvokeMethod(InvokeMethod node) {
344 setRefParent(node, node.receiver);
345 setRefParent(node, node.continuation);
346 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
347 }
348
349 processInvokeSuperMethod(InvokeSuperMethod node) {
350 setRefParent(node, node.continuation);
351 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
352 }
353
354 processInvokeConstructor(InvokeConstructor node) {
355 setRefParent(node, node.continuation);
356 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
357 }
358
359 processConcatenateStrings(ConcatenateStrings node) {
360 setRefParent(node, node.continuation);
361 node.arguments.forEach((Reference ref) => setRefParent(node, ref));
362 }
363
364 processBranch(Branch node) {
365 setParent(node, node.condition);
366 setRefParent(node, node.trueContinuation);
367 setRefParent(node, node.falseContinuation);
368 }
369
370 processTypeOperator(TypeOperator node) {
371 setRefParent(node, node.continuation);
372 setRefParent(node, node.receiver);
373 }
374
375 processSetClosureVariable(SetClosureVariable node) {
376 setParent(node, node.body);
377 setRefParent(node, node.value);
378 }
379
380 processDeclareFunction(DeclareFunction node) {
381 setParent(node, node.definition);
382 setParent(node, node.body);
383 }
384
385 // Definitions.
386
387 processLiteralList(LiteralList node) {
388 node.values.forEach((Reference ref) => setRefParent(node, ref));
389 }
390
391 processLiteralMap(LiteralMap node) {
392 node.values.forEach((Reference ref) => setRefParent(node, ref));
393 node.keys.forEach((Reference ref) => setRefParent(node, ref));
394 }
395
396 processCreateFunction(CreateFunction node) {
397 setParent(node, node.definition);
398 }
399
400 processContinuation(Continuation node) {
401 setParent(node, node.body);
402 node.parameters.forEach((Parameter param) => setParent(node, param));
403 }
404
405 // Conditions.
406
407 processIsTrue(IsTrue node) {
408 setRefParent(node, node.value);
409 }
410 }
411
412 class _ReductionKind {
413 final String name;
414 final int hashCode;
415
416 const _ReductionKind(this.name, this.hashCode);
417
418 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0);
419 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1);
420 static const _ReductionKind BETA_CONT_LIN =
421 const _ReductionKind('beta-cont-lin', 2);
422 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3);
423
424 String toString() => name;
425 }
426
427 /// Represents a reduction task on the worklist. Implements both hashCode and
428 /// operator== since instatiations are used as Set elements.
sigurdm 2014/08/01 09:09:41 typo: instatiations => instantiations
jgruber1 2014/08/07 09:07:36 Done.
429 class _ReductionTask {
430 final _ReductionKind kind;
431 final Node node;
432
433 int get hashCode {
434 assert(kind.hashCode < (1 << 2));
435 return (node.hashCode << 2) | kind.hashCode;
436 }
437
438 _ReductionTask(this.kind, this.node);
439
440 bool operator==(_ReductionTask that) {
441 return (that.kind == this.kind && that.node == this.node);
442 }
443
444 String toString() => "$kind: $node";
445 }
446
447 /// A dummy class used solely to mark nodes as deleted once they are removed
448 /// from a term.
449 class _DeletedNode extends Node {
450 accept(_) => null;
451 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698