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

Side by Side Diff: pkg/compiler/lib/src/cps_ir/shrinking_reductions.dart

Issue 1616673002: dart2js cps: Debugging utility and fix idempotency in shrinking reducer. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Do not run the same Pass instance twice Created 4 years, 10 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) 2014, the Dart project authors. Please see the AUTHORS file 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 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 dart2js.cps_ir.shrinking_reductions; 5 library dart2js.cps_ir.shrinking_reductions;
6 6
7 import 'cps_ir_nodes.dart'; 7 import 'cps_ir_nodes.dart';
8 import 'optimizers.dart'; 8 import 'optimizers.dart';
9 import 'cps_fragment.dart';
10 import '../constants/values.dart' as values;
11 9
12 /** 10 /**
13 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described 11 * [ShrinkingReducer] applies shrinking reductions to CPS terms as described
14 * in 'Compiling with Continuations, Continued' by Andrew Kennedy. 12 * in 'Compiling with Continuations, Continued' by Andrew Kennedy.
15 */ 13 */
16 class ShrinkingReducer extends Pass { 14 class ShrinkingReducer extends Pass {
17 String get passName => 'Shrinking reductions'; 15 String get passName => 'Shrinking reductions';
18 16
19 List<_ReductionTask> _worklist; 17 final List<_ReductionTask> _worklist = new List<_ReductionTask>();
20
21 static final _DeletedNode _DELETED = new _DeletedNode();
22 18
23 /// Applies shrinking reductions to root, mutating root in the process. 19 /// Applies shrinking reductions to root, mutating root in the process.
24 @override 20 @override
25 void rewrite(FunctionDefinition root) { 21 void rewrite(FunctionDefinition root) {
26 _worklist = new List<_ReductionTask>();
27 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist); 22 _RedexVisitor redexVisitor = new _RedexVisitor(_worklist);
28 23
29 // Sweep over the term, collecting redexes into the worklist. 24 // Sweep over the term, collecting redexes into the worklist.
30 redexVisitor.visit(root); 25 redexVisitor.visit(root);
31 26
32 // Process the worklist. 27 _iterateWorklist();
28 }
29
30 void _iterateWorklist() {
33 while (_worklist.isNotEmpty) { 31 while (_worklist.isNotEmpty) {
34 _ReductionTask task = _worklist.removeLast(); 32 _ReductionTask task = _worklist.removeLast();
35 _processTask(task); 33 _processTask(task);
36 } 34 }
37 } 35 }
38 36
37 /// Call instead of [_iterateWorklist] to check at every step that no
38 /// redex was missed.
39 void _debugWorklist(FunctionDefinition root) {
40 while (_worklist.isNotEmpty) {
41 _ReductionTask task = _worklist.removeLast();
42 String irBefore = root.debugString({
43 task.node: '${task.kind} applied here'
44 });
45 _processTask(task);
46 Set seenRedexes = _worklist.where(isValidTask).toSet();
47 Set actualRedexes = (new _RedexVisitor([])..visit(root)).worklist.toSet();
48 if (!seenRedexes.containsAll(actualRedexes)) {
49 _ReductionTask missedTask =
50 actualRedexes.firstWhere((x) => !seenRedexes.contains(x));
51 print('\nBEFORE $task:\n');
52 print(irBefore);
53 print('\nAFTER $task:\n');
54 root.debugPrint({
55 missedTask.node: 'MISSED ${missedTask.kind}'
56 });
57 throw 'Missed $missedTask after processing $task';
58 }
59 }
60 }
61
62 bool isValidTask(_ReductionTask task) {
63 switch (task.kind) {
64 case _ReductionKind.DEAD_VAL:
65 return _isDeadVal(task.node);
66 case _ReductionKind.DEAD_CONT:
67 return _isDeadCont(task.node);
68 case _ReductionKind.BETA_CONT_LIN:
69 return _isBetaContLin(task.node);
70 case _ReductionKind.ETA_CONT:
71 return _isEtaCont(task.node);
72 case _ReductionKind.DEAD_PARAMETER:
73 return _isDeadParameter(task.node);
74 case _ReductionKind.BRANCH:
75 return _isBranchRedex(task.node);
76 }
77 }
78
39 /// Removes the given node from the CPS graph, replacing it with its body 79 /// Removes the given node from the CPS graph, replacing it with its body
40 /// and marking it as deleted. The node's parent must be a [[InteriorNode]]. 80 /// and marking it as deleted. The node's parent must be a [[InteriorNode]].
41 void _removeNode(InteriorNode node) { 81 void _removeNode(InteriorNode node) {
42 Node body = node.body; 82 Node body = node.body;
43 InteriorNode parent = node.parent; 83 InteriorNode parent = node.parent;
44 assert(parent.body == node); 84 assert(parent.body == node);
45 85
46 body.parent = parent; 86 body.parent = parent;
47 parent.body = body; 87 parent.body = body;
48 node.parent = _DELETED; 88 node.parent = null;
89
90 // The removed node could be the last node between a continuation and
91 // an InvokeContinuation in the body.
92 if (parent is Continuation) {
93 _checkEtaCont(parent);
94 _checkUselessBranchTarget(parent);
95 }
49 } 96 }
50 97
51 /// Remove a given continuation from the CPS graph. The LetCont itself is 98 /// Remove a given continuation from the CPS graph. The LetCont itself is
52 /// removed if the given continuation is the only binding. 99 /// removed if the given continuation is the only binding.
53 void _removeContinuation(Continuation cont) { 100 void _removeContinuation(Continuation cont) {
54 LetCont parent = cont.parent; 101 LetCont parent = cont.parent;
55 if (parent.continuations.length == 1) { 102 if (parent.continuations.length == 1) {
56 _removeNode(parent); 103 _removeNode(parent);
57 } else { 104 } else {
58 parent.continuations.remove(cont); 105 parent.continuations.remove(cont);
59 } 106 }
60 cont.parent = _DELETED; 107 cont.parent = null;
61 } 108 }
62 109
63 void _processTask(_ReductionTask task) { 110 void _processTask(_ReductionTask task) {
64 // Skip tasks for deleted nodes. 111 // Skip tasks for deleted nodes.
65 if (task.node.parent == _DELETED) { 112 if (task.node.parent == null) {
66 return; 113 return;
67 } 114 }
68 115
69 switch (task.kind) { 116 switch (task.kind) {
70 case _ReductionKind.DEAD_VAL: 117 case _ReductionKind.DEAD_VAL:
71 _reduceDeadVal(task); 118 _reduceDeadVal(task);
72 break; 119 break;
73 case _ReductionKind.DEAD_CONT: 120 case _ReductionKind.DEAD_CONT:
74 _reduceDeadCont(task); 121 _reduceDeadCont(task);
75 break; 122 break;
(...skipping 10 matching lines...) Expand all
86 _reduceBranch(task); 133 _reduceBranch(task);
87 break; 134 break;
88 default: 135 default:
89 assert(false); 136 assert(false);
90 } 137 }
91 } 138 }
92 139
93 /// Applies the dead-val reduction: 140 /// Applies the dead-val reduction:
94 /// letprim x = V in E -> E (x not free in E). 141 /// letprim x = V in E -> E (x not free in E).
95 void _reduceDeadVal(_ReductionTask task) { 142 void _reduceDeadVal(_ReductionTask task) {
143 if (_isRemoved(task.node)) return;
96 assert(_isDeadVal(task.node)); 144 assert(_isDeadVal(task.node));
97 145
98 // Remove dead primitive. 146 LetPrim deadLet = task.node;
99 LetPrim letPrim = task.node; 147 Primitive deadPrim = deadLet.primitive;
100 destroyRefinementsOfDeadPrimitive(letPrim.primitive); 148 assert(deadPrim.hasNoRefinedUses);
101 _removeNode(letPrim); 149 // The node has no effective uses but can have refinement uses, which
150 // themselves can have more refinements uses (but only refinement uses).
151 // We must remove the entire refinement tree while looking for redexes
152 // whenever we remove one.
153 List<Primitive> deadlist = <Primitive>[deadPrim];
154 while (deadlist.isNotEmpty) {
155 Primitive node = deadlist.removeLast();
156 while (node.firstRef != null) {
157 Reference ref = node.firstRef;
158 Refinement use = ref.parent;
159 deadlist.add(use);
160 ref.unlink();
161 }
162 LetPrim binding = node.parent;
163 _removeNode(binding); // Remove the binding and check for eta redexes.
164 }
102 165
103 // Perform bookkeeping on removed body and scan for new redexes. 166 // Perform bookkeeping on removed body and scan for new redexes.
104 new _RemovalVisitor(_worklist).visit(letPrim.primitive); 167 new _RemovalVisitor(_worklist).visit(deadPrim);
105 } 168 }
106 169
107 /// Applies the dead-cont reduction: 170 /// Applies the dead-cont reduction:
108 /// letcont k x = E0 in E1 -> E1 (k not free in E1). 171 /// letcont k x = E0 in E1 -> E1 (k not free in E1).
109 void _reduceDeadCont(_ReductionTask task) { 172 void _reduceDeadCont(_ReductionTask task) {
110 assert(_isDeadCont(task.node)); 173 assert(_isDeadCont(task.node));
111 174
112 // Remove dead continuation. 175 // Remove dead continuation.
113 Continuation cont = task.node; 176 Continuation cont = task.node;
114 _removeContinuation(cont); 177 _removeContinuation(cont);
115 178
116 // Perform bookkeeping on removed body and scan for new redexes. 179 // Perform bookkeeping on removed body and scan for new redexes.
117 new _RemovalVisitor(_worklist).visit(cont); 180 new _RemovalVisitor(_worklist).visit(cont);
118 } 181 }
119 182
120 /// Applies the beta-cont-lin reduction: 183 /// Applies the beta-cont-lin reduction:
121 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1). 184 /// letcont k x = E0 in E1[k y] -> E1[E0[y/x]] (k not free in E1).
122 void _reduceBetaContLin(_ReductionTask task) { 185 void _reduceBetaContLin(_ReductionTask task) {
123 // Might have been mutated, recheck if reduction is still valid. 186 // Might have been mutated, recheck if reduction is still valid.
124 // In the following example, the beta-cont-lin reduction of k0 could have 187 // In the following example, the beta-cont-lin reduction of k0 could have
125 // been invalidated by removal of the dead continuation k1: 188 // been invalidated by removal of the dead continuation k1:
126 // 189 //
127 // letcont k0 x0 = E0 in 190 // letcont k0 x0 = E0 in
128 // letcont k1 x1 = k0 x1 in 191 // letcont k1 x1 = k0 x1 in
129 // return x2 192 // return x2
130 if (!_isBetaContLin(task.node)) { 193 if (!_isBetaContLin(task.node)) {
131 return; 194 return;
132 } 195 }
133 196
134 // Remove the continuation.
135 Continuation cont = task.node; 197 Continuation cont = task.node;
136 _removeContinuation(cont);
137
138 // Replace its invocation with the continuation body.
139 InvokeContinuation invoke = cont.firstRef.parent; 198 InvokeContinuation invoke = cont.firstRef.parent;
140 InteriorNode invokeParent = invoke.parent; 199 InteriorNode invokeParent = invoke.parent;
200 Expression body = cont.body;
141 201
142 cont.body.parent = invokeParent; 202 // Replace the invocation with the continuation body.
143 invokeParent.body = cont.body; 203 invokeParent.body = body;
204 body.parent = invokeParent;
205 cont.body = null;
144 206
145 // Substitute the invocation argument for the continuation parameter. 207 // Substitute the invocation argument for the continuation parameter.
146 for (int i = 0; i < invoke.arguments.length; i++) { 208 for (int i = 0; i < invoke.arguments.length; i++) {
147 cont.parameters[i].replaceUsesWith(invoke.arguments[i].definition); 209 Parameter param = cont.parameters[i];
148 invoke.arguments[i].definition.useElementAsHint(cont.parameters[i].hint); 210 Primitive argument = invoke.arguments[i].definition;
211 param.replaceUsesWith(argument);
212 argument.useElementAsHint(param.hint);
213 _checkConstantBranchCondition(argument);
149 } 214 }
150 215
216 // Remove the continuation after inlining it so we can check for eta redexes
217 // which may arise after removing the LetCont.
218 _removeContinuation(cont);
219
151 // Perform bookkeeping on substituted body and scan for new redexes. 220 // Perform bookkeeping on substituted body and scan for new redexes.
152 new _RemovalVisitor(_worklist).visit(invoke); 221 new _RemovalVisitor(_worklist).visit(invoke);
222
223 if (invokeParent is Continuation) {
224 _checkEtaCont(invokeParent);
225 _checkUselessBranchTarget(invokeParent);
226 }
153 } 227 }
154 228
155 /// Applies the eta-cont reduction: 229 /// Applies the eta-cont reduction:
156 /// letcont k x = j x in E -> E[j/k]. 230 /// letcont k x = j x in E -> E[j/k].
157 /// If k is unused, degenerates to dead-cont. 231 /// If k is unused, degenerates to dead-cont.
158 void _reduceEtaCont(_ReductionTask task) { 232 void _reduceEtaCont(_ReductionTask task) {
159 // Might have been mutated, recheck if reduction is still valid. 233 // Might have been mutated, recheck if reduction is still valid.
160 // In the following example, the eta-cont reduction of k1 could have been 234 // In the following example, the eta-cont reduction of k1 could have been
161 // invalidated by an earlier beta-cont-lin reduction of k0. 235 // invalidated by an earlier beta-cont-lin reduction of k0.
162 // 236 //
(...skipping 18 matching lines...) Expand all
181 // cont will be as well, after the reduction. 255 // cont will be as well, after the reduction.
182 if (invoke.isEscapingTry) { 256 if (invoke.isEscapingTry) {
183 Reference current = cont.firstRef; 257 Reference current = cont.firstRef;
184 while (current != null) { 258 while (current != null) {
185 InvokeContinuation owner = current.parent; 259 InvokeContinuation owner = current.parent;
186 owner.isEscapingTry = true; 260 owner.isEscapingTry = true;
187 current = current.next; 261 current = current.next;
188 } 262 }
189 } 263 }
190 264
191 // Replace all occurrences with the wrapped continuation. 265 // Replace all occurrences with the wrapped continuation and find redexes.
192 cont.replaceUsesWith(wrappedCont); 266 while (cont.firstRef != null) {
267 Reference ref = cont.firstRef;
268 ref.changeTo(wrappedCont);
269 Node use = ref.parent;
270 if (use is InvokeContinuation && use.parent is Continuation) {
271 _checkUselessBranchTarget(use.parent);
272 }
273 }
193 274
194 // Perform bookkeeping on removed body and scan for new redexes. 275 // Perform bookkeeping on removed body and scan for new redexes.
195 new _RemovalVisitor(_worklist).visit(cont); 276 new _RemovalVisitor(_worklist).visit(cont);
196 } 277 }
197 278
198 void _reduceBranch(_ReductionTask task) { 279 void _reduceBranch(_ReductionTask task) {
199 Branch branch = task.node; 280 Branch branch = task.node;
200 // Replace Branch with InvokeContinuation of one of the targets. When the 281 // Replace Branch with InvokeContinuation of one of the targets. When the
201 // branch is deleted the other target becomes unreferenced and the chosen 282 // branch is deleted the other target becomes unreferenced and the chosen
202 // target becomes available for eta-cont and further reductions. 283 // target becomes available for eta-cont and further reductions.
203 Continuation target; 284 Continuation target;
204 Primitive condition = branch.condition.definition; 285 Primitive condition = branch.condition.definition;
205 if (condition is Constant) { 286 if (condition is Constant) {
206 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck) 287 target = isTruthyConstant(condition.value, strict: branch.isStrictCheck)
207 ? branch.trueContinuation.definition 288 ? branch.trueContinuation.definition
208 : branch.falseContinuation.definition; 289 : branch.falseContinuation.definition;
209 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation.definition)) { 290 } else if (_isBranchTargetOfUselessIf(branch.trueContinuation.definition)) {
210 target = branch.trueContinuation.definition; 291 target = branch.trueContinuation.definition;
211 } else { 292 } else {
212 return; 293 return;
213 } 294 }
214 295
215 InvokeContinuation invoke = new InvokeContinuation( 296 InvokeContinuation invoke = new InvokeContinuation(
216 target, <Primitive>[] 297 target, <Primitive>[]
217 // TODO(sra): Add sourceInformation. 298 // TODO(sra): Add sourceInformation.
218 /*, sourceInformation: branch.sourceInformation*/); 299 /*, sourceInformation: branch.sourceInformation*/);
219 branch.parent.body = invoke; 300 branch.parent.body = invoke;
220 invoke.parent = branch.parent; 301 invoke.parent = branch.parent;
221 branch.parent = _DELETED; 302 branch.parent = null;
222 303
223 new _RemovalVisitor(_worklist).visit(branch); 304 new _RemovalVisitor(_worklist).visit(branch);
224 } 305 }
225 306
226 void _reduceDeadParameter(_ReductionTask task) { 307 void _reduceDeadParameter(_ReductionTask task) {
227 // Continuation eta-reduction can destroy a dead parameter redex. For 308 // Continuation eta-reduction can destroy a dead parameter redex. For
228 // example, in the term: 309 // example, in the term:
229 // 310 //
230 // let cont k0(v0) = /* v0 is not used */ in 311 // let cont k0(v0) = /* v0 is not used */ in
231 // let cont k1(v1) = k0(v1) in 312 // let cont k1(v1) = k0(v1) in
232 // call foo () k1 313 // call foo () k1
233 // 314 //
234 // Continuation eta-reduction of k1 gives: 315 // Continuation eta-reduction of k1 gives:
235 // 316 //
236 // let cont k0(v0) = /* v0 is not used */ in 317 // let cont k0(v0) = /* v0 is not used */ in
237 // call foo () k0 318 // call foo () k0
238 // 319 //
239 // Where the dead parameter reduction is no longer valid because we do not 320 // Where the dead parameter reduction is no longer valid because we do not
240 // allow removing the paramter of call continuations. We disallow such eta 321 // allow removing the paramter of call continuations. We disallow such eta
241 // reductions in [_isEtaCont]. 322 // reductions in [_isEtaCont].
242 assert(_isDeadParameter(task.node)); 323 Parameter parameter = task.node;
324 if (_isParameterRemoved(parameter)) return;
325 assert(_isDeadParameter(parameter));
243 326
244 Parameter parameter = task.node;
245 Continuation continuation = parameter.parent; 327 Continuation continuation = parameter.parent;
246 int index = continuation.parameters.indexOf(parameter); 328 int index = continuation.parameters.indexOf(parameter);
247 assert(index != -1); 329 assert(index != -1);
330 continuation.parameters.removeAt(index);
331 parameter.parent = null; // Mark as removed.
248 332
249 // Remove the index'th argument from each invocation. 333 // Remove the index'th argument from each invocation.
250 Reference<Continuation> current = continuation.firstRef; 334 for (Reference ref = continuation.firstRef; ref != null; ref = ref.next) {
251 while (current != null) { 335 InvokeContinuation invoke = ref.parent;
252 InvokeContinuation invoke = current.parent;
253 Reference<Primitive> argument = invoke.arguments[index]; 336 Reference<Primitive> argument = invoke.arguments[index];
254 argument.unlink(); 337 argument.unlink();
255 // Removing an argument can create a dead parameter or dead value redex. 338 invoke.arguments.removeAt(index);
256 if (argument.definition is Parameter) { 339 // Removing an argument can create a dead primitive or an eta-redex
257 if (_isDeadParameter(argument.definition)) { 340 // in case the parent is a continuation that now has matching parameters.
258 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, 341 _checkDeadPrimitive(argument.definition);
259 argument.definition)); 342 if (invoke.parent is Continuation) {
260 } 343 _checkEtaCont(invoke.parent);
261 } else { 344 _checkUselessBranchTarget(invoke.parent);
262 Node parent = argument.definition.parent;
263 if (parent is LetPrim) {
264 if (_isDeadVal(parent)) {
265 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent));
266 }
267 }
268 } 345 }
269 invoke.arguments.removeAt(index);
270 current = current.next;
271 } 346 }
272 continuation.parameters.removeAt(index);
273 347
274 // Removing an unused parameter can create an eta-redex. 348 // Removing an unused parameter can create an eta-redex, in case the
349 // body is an InvokeContinuation that now has matching arguments.
350 _checkEtaCont(continuation);
351 }
352
353 void _checkEtaCont(Continuation continuation) {
275 if (_isEtaCont(continuation)) { 354 if (_isEtaCont(continuation)) {
276 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation)); 355 _worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, continuation));
277 } 356 }
278 } 357 }
358
359 void _checkUselessBranchTarget(Continuation continuation) {
360 if (_isBranchTargetOfUselessIf(continuation)) {
361 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH,
362 continuation.firstRef.parent));
363 }
364 }
365
366 void _checkConstantBranchCondition(Primitive primitive) {
367 if (primitive is! Constant) return;
368 for (Reference ref = primitive.firstRef; ref != null; ref = ref.next) {
369 Node use = ref.parent;
370 if (use is Branch) {
371 _worklist.add(new _ReductionTask(_ReductionKind.BRANCH, use));
372 }
373 }
374 }
375
376 void _checkDeadPrimitive(Primitive primitive) {
377 primitive = primitive.unrefined;
378 if (primitive is Parameter) {
379 if (_isDeadParameter(primitive)) {
380 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER,
381 primitive));
382 }
383 } else if (primitive.parent is LetPrim) {
384 LetPrim letPrim = primitive.parent;
385 if (_isDeadVal(letPrim)) {
386 _worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, letPrim));
387 }
388 }
389 }
390 }
391
392 bool _isRemoved(InteriorNode node) {
393 return node.parent == null;
394 }
395
396 bool _isParameterRemoved(Parameter parameter) {
397 // A parameter can be removed directly or because its continuation is removed.
398 return parameter.parent == null || _isRemoved(parameter.parent);
279 } 399 }
280 400
281 /// Returns true iff the bound primitive is unused, and has no effects 401 /// Returns true iff the bound primitive is unused, and has no effects
282 /// preventing it from being eliminated. 402 /// preventing it from being eliminated.
283 bool _isDeadVal(LetPrim node) { 403 bool _isDeadVal(LetPrim node) {
284 return node.primitive.hasNoEffectiveUses && 404 return !_isRemoved(node) &&
405 node.primitive.hasNoRefinedUses &&
285 node.primitive.isSafeForElimination; 406 node.primitive.isSafeForElimination;
286 } 407 }
287 408
288 /// Returns true iff the continuation is unused. 409 /// Returns true iff the continuation is unused.
289 bool _isDeadCont(Continuation cont) { 410 bool _isDeadCont(Continuation cont) {
290 return !cont.isReturnContinuation && !cont.hasAtLeastOneUse; 411 return !_isRemoved(cont) &&
412 !cont.isReturnContinuation &&
413 !cont.hasAtLeastOneUse;
291 } 414 }
292 415
293 /// Returns true iff the continuation has a body (i.e., it is not the return 416 /// Returns true iff the continuation has a body (i.e., it is not the return
294 /// continuation), it is used exactly once, and that use is as the continuation 417 /// continuation), it is used exactly once, and that use is as the continuation
295 /// of a continuation invocation. 418 /// of a continuation invocation.
296 bool _isBetaContLin(Continuation cont) { 419 bool _isBetaContLin(Continuation cont) {
420 if (_isRemoved(cont)) return false;
421
297 // There is a restriction on continuation eta-redexes that the body is not an 422 // There is a restriction on continuation eta-redexes that the body is not an
298 // invocation of the return continuation, because that leads to worse code 423 // invocation of the return continuation, because that leads to worse code
299 // when translating back to direct style (it duplicates returns). There is no 424 // when translating back to direct style (it duplicates returns). There is no
300 // such restriction here because continuation beta-reduction is only performed 425 // such restriction here because continuation beta-reduction is only performed
301 // for singly referenced continuations. Thus, there is no possibility of code 426 // for singly referenced continuations. Thus, there is no possibility of code
302 // duplication. 427 // duplication.
303 if (cont.isReturnContinuation || !cont.hasExactlyOneUse) { 428 if (cont.isReturnContinuation || !cont.hasExactlyOneUse) {
304 return false; 429 return false;
305 } 430 }
306 431
307 if (cont.firstRef.parent is! InvokeContinuation) return false; 432 if (cont.firstRef.parent is! InvokeContinuation) return false;
308 433
309 InvokeContinuation invoke = cont.firstRef.parent; 434 InvokeContinuation invoke = cont.firstRef.parent;
310 if (cont != invoke.continuation.definition) return false;
311 435
312 // Beta-reduction will move the continuation's body to its unique invocation 436 // Beta-reduction will move the continuation's body to its unique invocation
313 // site. This is not safe if the body is moved into an exception handler 437 // site. This is not safe if the body is moved into an exception handler
314 // binding. Search from the invocation to the continuation binding to 438 // binding.
315 // make sure that there is no binding for a handler. 439 if (invoke.isEscapingTry) return false;
316 Node current = invoke.parent; 440
317 while (current != cont.parent) {
318 // There is no need to reduce a beta-redex inside a deleted subterm.
319 if (current == ShrinkingReducer._DELETED) return false;
320 if (current is LetHandler) return false;
321 current = current.parent;
322 }
323 return true; 441 return true;
324 } 442 }
325 443
326 /// Returns true iff the continuation consists of a continuation 444 /// Returns true iff the continuation consists of a continuation
327 /// invocation, passing on all parameters. Special cases exist (see below). 445 /// invocation, passing on all parameters. Special cases exist (see below).
328 bool _isEtaCont(Continuation cont) { 446 bool _isEtaCont(Continuation cont) {
329 if (cont.isReturnContinuation || cont.body is! InvokeContinuation) { 447 if (_isRemoved(cont)) return false;
448
449 if (!cont.isJoinContinuation || cont.body is! InvokeContinuation) {
330 return false; 450 return false;
331 } 451 }
332 452
333 InvokeContinuation invoke = cont.body; 453 InvokeContinuation invoke = cont.body;
334 Continuation invokedCont = invoke.continuation.definition; 454 Continuation invokedCont = invoke.continuation.definition;
335 455
336 // Do not eta-reduce return join-points since the direct-style code is worse 456 // Do not eta-reduce return join-points since the direct-style code is worse
337 // in the common case (i.e. returns are moved inside `if` branches). 457 // in the common case (i.e. returns are moved inside `if` branches).
338 if (invokedCont.isReturnContinuation) { 458 if (invokedCont.isReturnContinuation) {
339 return false; 459 return false;
340 } 460 }
341 461
342 // Do not perform reductions replace a function call continuation with a
343 // non-call continuation. The invoked continuation is definitely not a call
344 // continuation, because it has a direct invocation in this continuation's
345 // body.
346 bool isCallContinuation(Continuation continuation) {
347 Reference<Continuation> current = cont.firstRef;
348 while (current != null) {
349 if (current.parent is InvokeContinuation) {
350 InvokeContinuation invoke = current.parent;
351 if (invoke.continuation.definition == continuation) return false;
352 }
353 current = current.next;
354 }
355 return true;
356 }
357 if (isCallContinuation(cont)) {
358 return false;
359 }
360
361 // Translation to direct style generates different statements for recursive 462 // Translation to direct style generates different statements for recursive
362 // and non-recursive invokes. It should still be possible to apply eta-cont if 463 // and non-recursive invokes. It should still be possible to apply eta-cont if
363 // this is not a self-invocation. 464 // this is not a self-invocation.
364 // 465 //
365 // TODO(kmillikin): Remove this restriction if it makes sense to do so. 466 // TODO(kmillikin): Remove this restriction if it makes sense to do so.
366 if (invoke.isRecursive) { 467 if (invoke.isRecursive) {
367 return false; 468 return false;
368 } 469 }
369 470
370 // If cont has more parameters than the invocation has arguments, the extra 471 // If cont has more parameters than the invocation has arguments, the extra
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
406 Expression _unfoldDeadRefinements(Expression node) { 507 Expression _unfoldDeadRefinements(Expression node) {
407 while (node is LetPrim) { 508 while (node is LetPrim) {
408 LetPrim let = node; 509 LetPrim let = node;
409 Primitive prim = let.primitive; 510 Primitive prim = let.primitive;
410 if (prim.hasAtLeastOneUse || prim is! Refinement) return node; 511 if (prim.hasAtLeastOneUse || prim is! Refinement) return node;
411 node = node.next; 512 node = node.next;
412 } 513 }
413 return node; 514 return node;
414 } 515 }
415 516
517 bool _isBranchRedex(Branch branch) {
518 return _isUselessIf(branch) || branch.condition.definition is Constant;
519 }
520
416 bool _isBranchTargetOfUselessIf(Continuation cont) { 521 bool _isBranchTargetOfUselessIf(Continuation cont) {
417 // A useless-if has an empty then and else branch, e.g. `if (cond);`. 522 // A useless-if has an empty then and else branch, e.g. `if (cond);`.
418 // 523 //
419 // Detect T or F in 524 // Detect T or F in
420 // 525 //
421 // let cont Join() = ... 526 // let cont Join() = ...
422 // in let cont T() = Join() 527 // in let cont T() = Join()
423 // F() = Join() 528 // F() = Join()
424 // in branch condition T F 529 // in branch condition T F
425 // 530 //
426 if (!cont.hasExactlyOneUse) return false; 531 if (!cont.hasExactlyOneUse) return false;
427 if (cont.firstRef.parent is! Branch) return false; 532 Node use = cont.firstRef.parent;
428 Branch branch = cont.firstRef.parent; 533 if (use is! Branch) return false;
534 return _isUselessIf(use);
535 }
429 536
430 // Are both continuations the same InvokeContinuation on a join? 537 bool _isUselessIf(Branch branch) {
431 Continuation trueCont = branch.trueContinuation.definition; 538 Continuation trueCont = branch.trueContinuation.definition;
432 Expression trueBody = _unfoldDeadRefinements(trueCont.body); 539 Expression trueBody = _unfoldDeadRefinements(trueCont.body);
433 if (trueBody is! InvokeContinuation) return false; 540 if (trueBody is! InvokeContinuation) return false;
434 Continuation falseCont = branch.falseContinuation.definition; 541 Continuation falseCont = branch.falseContinuation.definition;
435 Expression falseBody = _unfoldDeadRefinements(falseCont.body); 542 Expression falseBody = _unfoldDeadRefinements(falseCont.body);
436 if (falseBody is! InvokeContinuation) return false; 543 if (falseBody is! InvokeContinuation) return false;
437 InvokeContinuation trueInvoke = trueBody; 544 InvokeContinuation trueInvoke = trueBody;
438 InvokeContinuation falseInvoke = falseBody; 545 InvokeContinuation falseInvoke = falseBody;
439 if (trueInvoke.continuation.definition != 546 if (trueInvoke.continuation.definition !=
440 falseInvoke.continuation.definition) { 547 falseInvoke.continuation.definition) {
441 return false; 548 return false;
442 } 549 }
443 assert(trueInvoke.arguments.length == falseInvoke.arguments.length); 550 assert(trueInvoke.arguments.length == falseInvoke.arguments.length);
444 // Matching zero arguments should be adequate, since isomorphic true and false 551 // Matching zero arguments should be adequate, since isomorphic true and false
445 // invocations should result in redundant phis which are removed elsewhere. 552 // invocations should result in redundant phis which are removed elsewhere.
446 if (trueInvoke.arguments.isNotEmpty) return false; 553 if (trueInvoke.arguments.isNotEmpty) return false;
447 return true; 554 return true;
448 } 555 }
449 556
450 bool _isDeadParameter(Parameter parameter) { 557 bool _isDeadParameter(Parameter parameter) {
558 if (_isParameterRemoved(parameter)) return false;
559
451 // We cannot remove function parameters as an intraprocedural optimization. 560 // We cannot remove function parameters as an intraprocedural optimization.
452 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) { 561 if (parameter.parent is! Continuation || parameter.hasAtLeastOneUse) {
453 return false; 562 return false;
454 } 563 }
455 564
456 // We cannot remove exception handler parameters, they have a fixed arity
457 // of two.
458 if (parameter.parent.parent is LetHandler) {
459 return false;
460 }
461
462 // We cannot remove the parameter to a call continuation, because the 565 // We cannot remove the parameter to a call continuation, because the
463 // resulting expression will not be well-formed (call continuations have 566 // resulting expression will not be well-formed (call continuations have
464 // exactly one argument). The return continuation is a call continuation, so 567 // exactly one argument). The return continuation is a call continuation, so
465 // we cannot remove its dummy parameter. 568 // we cannot remove its dummy parameter.
466 Continuation continuation = parameter.parent; 569 Continuation continuation = parameter.parent;
467 if (continuation.isReturnContinuation) return false; 570 if (!continuation.isJoinContinuation) return false;
468 Reference<Continuation> current = continuation.firstRef; 571
469 while (current != null) {
470 if (current.parent is! InvokeContinuation) return false;
471 InvokeContinuation invoke = current.parent;
472 if (invoke.continuation.definition != continuation) return false;
473 current = current.next;
474 }
475 return true; 572 return true;
476 } 573 }
477 574
478 /// Traverses a term and adds any found redexes to the worklist. 575 /// Traverses a term and adds any found redexes to the worklist.
479 class _RedexVisitor extends TrampolineRecursiveVisitor { 576 class _RedexVisitor extends TrampolineRecursiveVisitor {
480 final List<_ReductionTask> worklist; 577 final List<_ReductionTask> worklist;
481 578
482 _RedexVisitor(this.worklist); 579 _RedexVisitor(this.worklist);
483 580
484 void processLetPrim(LetPrim node) { 581 void processLetPrim(LetPrim node) {
485 if (_isDeadVal(node)) { 582 if (_isDeadVal(node)) {
486 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node)); 583 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, node));
487 } 584 }
488 } 585 }
489 586
490 void processBranch(Branch node) { 587 void processBranch(Branch node) {
491 if (node.condition.definition is Constant) { 588 if (_isBranchRedex(node)) {
492 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, node)); 589 worklist.add(new _ReductionTask(_ReductionKind.BRANCH, node));
493 } 590 }
494 } 591 }
495 592
496 void processContinuation(Continuation node) { 593 void processContinuation(Continuation node) {
497 // While it would be nice to remove exception handlers that are provably 594 // While it would be nice to remove exception handlers that are provably
498 // unnecessary (e.g., the body cannot throw), that takes more sophisticated 595 // unnecessary (e.g., the body cannot throw), that takes more sophisticated
499 // analysis than we do in this pass. 596 // analysis than we do in this pass.
500 if (node.parent is LetHandler) return; 597 if (node.parent is LetHandler) return;
501 598
502 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex 599 // Continuation beta- and eta-redexes can overlap, namely when an eta-redex
503 // is invoked exactly once. We prioritize continuation beta-redexes over 600 // is invoked exactly once. We prioritize continuation beta-redexes over
504 // eta-redexes because some reductions (e.g., dead parameter elimination) 601 // eta-redexes because some reductions (e.g., dead parameter elimination)
505 // can destroy a continuation eta-redex. If we prioritized eta- over 602 // can destroy a continuation eta-redex. If we prioritized eta- over
506 // beta-redexes, this would implicitly "create" the corresponding beta-redex 603 // beta-redexes, this would implicitly "create" the corresponding beta-redex
507 // (in the sense that it would still apply) and the algorithm would not 604 // (in the sense that it would still apply) and the algorithm would not
508 // detect it. 605 // detect it.
509 if (_isDeadCont(node)) { 606 if (_isDeadCont(node)) {
510 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node)); 607 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, node));
511 } else if (_isBetaContLin(node)){ 608 } else if (_isBetaContLin(node)){
512 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node)); 609 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, node));
513 } else if (_isEtaCont(node)) { 610 } else if (_isEtaCont(node)) {
514 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node)); 611 worklist.add(new _ReductionTask(_ReductionKind.ETA_CONT, node));
515 } else if (_isBranchTargetOfUselessIf(node)) {
516 worklist.add(new _ReductionTask(_ReductionKind.BRANCH,
517 node.firstRef.parent));
518 } 612 }
519 } 613 }
520 614
521 void processParameter(Parameter node) { 615 void processParameter(Parameter node) {
522 if (_isDeadParameter(node)) { 616 if (_isDeadParameter(node)) {
523 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node)); 617 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER, node));
524 } 618 }
525 } 619 }
526 } 620 }
527 621
528 /// Traverses a deleted CPS term, marking nodes that might participate in a 622 /// Traverses a deleted CPS term, marking nodes that might participate in a
529 /// redex as deleted and adding newly created redexes to the worklist. 623 /// redex as deleted and adding newly created redexes to the worklist.
530 /// 624 ///
531 /// Deleted nodes that might participate in a reduction task are marked so that 625 /// Deleted nodes that might participate in a reduction task are marked so that
532 /// any corresponding tasks can be skipped. Nodes are marked so by setting 626 /// any corresponding tasks can be skipped. Nodes are marked so by setting
533 /// their parent to the deleted sentinel. 627 /// their parent to the deleted sentinel.
534 class _RemovalVisitor extends TrampolineRecursiveVisitor { 628 class _RemovalVisitor extends TrampolineRecursiveVisitor {
535 final List<_ReductionTask> worklist; 629 final List<_ReductionTask> worklist;
536 630
537 _RemovalVisitor(this.worklist); 631 _RemovalVisitor(this.worklist);
538 632
539 void processLetPrim(LetPrim node) { 633 void processLetPrim(LetPrim node) {
540 node.parent = ShrinkingReducer._DELETED; 634 node.parent = null;
541 } 635 }
542 636
543 void processContinuation(Continuation node) { 637 void processContinuation(Continuation node) {
544 node.parent = ShrinkingReducer._DELETED; 638 node.parent = null;
545 } 639 }
546 640
547 void processReference(Reference reference) { 641 void processReference(Reference reference) {
548 reference.unlink(); 642 reference.unlink();
549 643
550 if (reference.definition is Primitive) { 644 if (reference.definition is Primitive) {
551 Primitive primitive = reference.definition; 645 Primitive primitive = reference.definition.unrefined;
552 Node parent = primitive.parent; 646 Node parent = primitive.parent;
553 // The parent might be the deleted sentinel, or it might be a 647 // The parent might be the deleted sentinel, or it might be a
554 // Continuation or FunctionDefinition if the primitive is an argument. 648 // Continuation or FunctionDefinition if the primitive is an argument.
555 if (parent is LetPrim && _isDeadVal(parent)) { 649 if (parent is LetPrim && _isDeadVal(parent)) {
556 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent)); 650 worklist.add(new _ReductionTask(_ReductionKind.DEAD_VAL, parent));
651 } else if (primitive is Parameter && _isDeadParameter(primitive)) {
652 worklist.add(new _ReductionTask(_ReductionKind.DEAD_PARAMETER,
653 primitive));
557 } 654 }
558 } else if (reference.definition is Continuation) { 655 } else if (reference.definition is Continuation) {
559 Continuation cont = reference.definition; 656 Continuation cont = reference.definition;
560 Node parent = cont.parent; 657 Node parent = cont.parent;
561 // The parent might be the deleted sentinel, or it might be a 658 // The parent might be the deleted sentinel, or it might be a
562 // Body if the continuation is the return continuation. 659 // Body if the continuation is the return continuation.
563 if (parent is LetCont) { 660 if (parent is LetCont) {
564 if (cont.isRecursive && cont.hasAtMostOneUse) { 661 if (cont.isRecursive && cont.hasAtMostOneUse) {
565 // Convert recursive to nonrecursive continuations. If the 662 // Convert recursive to nonrecursive continuations. If the
566 // continuation is still in use, it is either dead and will be 663 // continuation is still in use, it is either dead and will be
567 // removed, or it is called nonrecursively outside its body. 664 // removed, or it is called nonrecursively outside its body.
568 cont.isRecursive = false; 665 cont.isRecursive = false;
569 } 666 }
570 if (_isDeadCont(cont)) { 667 if (_isDeadCont(cont)) {
571 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont)); 668 worklist.add(new _ReductionTask(_ReductionKind.DEAD_CONT, cont));
572 } else if (_isBetaContLin(cont)) { 669 } else if (_isBetaContLin(cont)) {
573 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont)); 670 worklist.add(new _ReductionTask(_ReductionKind.BETA_CONT_LIN, cont));
574 } else if (_isBranchTargetOfUselessIf(cont)) { 671 } else if (_isBranchTargetOfUselessIf(cont)) {
575 worklist.add( 672 worklist.add(
576 new _ReductionTask(_ReductionKind.BRANCH, cont.firstRef.parent)); 673 new _ReductionTask(_ReductionKind.BRANCH, cont.firstRef.parent));
577 } 674 }
578 } 675 }
579 } 676 }
580 } 677 }
581 } 678 }
582 679
583 680 enum _ReductionKind {
584 681 DEAD_VAL,
585 class _ReductionKind { 682 DEAD_CONT,
586 final String name; 683 BETA_CONT_LIN,
587 final int hashCode; 684 ETA_CONT,
588 685 DEAD_PARAMETER,
589 const _ReductionKind(this.name, this.hashCode); 686 BRANCH
590
591 static const _ReductionKind DEAD_VAL = const _ReductionKind('dead-val', 0);
592 static const _ReductionKind DEAD_CONT = const _ReductionKind('dead-cont', 1);
593 static const _ReductionKind BETA_CONT_LIN =
594 const _ReductionKind('beta-cont-lin', 2);
595 static const _ReductionKind ETA_CONT = const _ReductionKind('eta-cont', 3);
596 static const _ReductionKind DEAD_PARAMETER =
597 const _ReductionKind('dead-parameter', 4);
598 static const _ReductionKind BRANCH = const _ReductionKind('branch', 5);
599
600 String toString() => name;
601 } 687 }
602 688
603 /// Represents a reduction task on the worklist. Implements both hashCode and 689 /// Represents a reduction task on the worklist. Implements both hashCode and
604 /// operator== since instantiations are used as Set elements. 690 /// operator== since instantiations are used as Set elements.
605 class _ReductionTask { 691 class _ReductionTask {
606 final _ReductionKind kind; 692 final _ReductionKind kind;
607 final Node node; 693 final Node node;
608 694
609 int get hashCode { 695 int get hashCode {
610 assert(kind.hashCode < (1 << 3)); 696 return (node.hashCode << 3) | kind.index;
611 return (node.hashCode << 3) | kind.hashCode;
612 } 697 }
613 698
614 _ReductionTask(this.kind, this.node) { 699 _ReductionTask(this.kind, this.node) {
615 assert(node is Continuation || node is LetPrim || node is Parameter || 700 assert(node is Continuation || node is LetPrim || node is Parameter ||
616 node is Branch); 701 node is Branch);
617 } 702 }
618 703
619 bool operator==(_ReductionTask that) { 704 bool operator==(_ReductionTask that) {
620 return (that.kind == this.kind && that.node == this.node); 705 return (that.kind == this.kind && that.node == this.node);
621 } 706 }
622 707
623 String toString() => "$kind: $node"; 708 String toString() => "$kind: $node";
624 } 709 }
625
626 /// A dummy class used solely to mark nodes as deleted once they are removed
627 /// from a term.
628 class _DeletedNode extends Node {
629 accept(_) {}
630 setParentPointers() {}
631 }
OLDNEW
« no previous file with comments | « pkg/compiler/lib/src/cps_ir/redundant_join.dart ('k') | pkg/compiler/lib/src/cps_ir/type_propagation.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698