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

Side by Side Diff: pkg/dev_compiler/lib/src/compiler/js_metalet.dart

Issue 2980113002: fix #27320, better DDC temp generation (Closed)
Patch Set: rebase Created 3 years, 5 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) 2015, the Dart project authors. Please see the AUTHORS file 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 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 // TODO(jmesserly): import from its own package 5 // TODO(jmesserly): import from its own package
6 import '../js_ast/js_ast.dart'; 6 import '../js_ast/js_ast.dart';
7 7
8 import 'js_names.dart' show TemporaryId; 8 import 'js_names.dart' show TemporaryId;
9 9
10 /// A synthetic `let*` node, similar to that found in Scheme. 10 /// A synthetic `let*` node, similar to that found in Scheme.
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
58 var block = toStatement(); 58 var block = toStatement();
59 var s = block.statements; 59 var s = block.statements;
60 if (s.length == 1 && s.first is ExpressionStatement) { 60 if (s.length == 1 && s.first is ExpressionStatement) {
61 ExpressionStatement es = s.first; 61 ExpressionStatement es = s.first;
62 return es.expression; 62 return es.expression;
63 } 63 }
64 64
65 return _toInvokedFunction(block); 65 return _toInvokedFunction(block);
66 } 66 }
67 67
68 Expression toAssignExpression(Expression left) { 68 Expression toAssignExpression(Expression left, [String op]) {
69 if (left is Identifier) { 69 if (left is Identifier) {
70 var simple = _simplifyAssignment(left); 70 return _simplifyAssignment(left, op: op) ?? _toAssign(left, op);
71 if (simple != null) return simple; 71 } else if (left is PropertyAccess &&
72 left.receiver is This &&
73 (left.selector is Identifier || left.selector is LiteralString)) {
74 return _toAssign(left, op);
75 }
76 return super.toAssignExpression(left, op);
77 }
72 78
73 var exprs = body.toList(); 79 Expression _toAssign(Expression left, [String op]) {
74 exprs.add(exprs.removeLast().toAssignExpression(left)); 80 var exprs = body.toList();
75 return new MetaLet(variables, exprs); 81 exprs.add(exprs.removeLast().toAssignExpression(left, op));
76 } 82 return new MetaLet(variables, exprs);
77 return super.toAssignExpression(left);
78 } 83 }
79 84
80 Statement toVariableDeclaration(Identifier name) { 85 Statement toVariableDeclaration(Identifier name) {
81 var simple = _simplifyAssignment(name, isDeclaration: true); 86 var simple = _simplifyAssignment(name, isDeclaration: true);
82 if (simple != null) return simple.toStatement(); 87 if (simple != null) return simple.toStatement();
83 return super.toVariableDeclaration(name); 88
89 // We can still optimize something like:
90 //
91 // let x = ((l) => l == null ? null : l.xyz)(some.expr);
92 //
93 // can be transformed to:
94 //
95 // let l = some.expr;
96 // let x = l == null ? null : l.xyz;
97 //
98 // Because `x` is a declaration, we know it is safe to move.
99 // (see also _toAssign)
100 var statements = body
101 .map((e) =>
102 e == body.last ? e.toVariableDeclaration(name) : e.toStatement())
103 .toList();
104 return _finishStatement(statements);
84 } 105 }
85 106
86 Expression toExpression() { 107 Expression toExpression() {
87 if (_expression != null) return _expression; 108 if (_expression != null) return _expression;
88 var block = toReturn(); 109 var block = toReturn();
89 var s = block.statements; 110 var s = block.statements;
90 if (s.length == 1 && s.first is Return) { 111 if (s.length == 1 && s.first is Return) {
91 Return es = s.first; 112 Return es = s.first;
92 return _expression = es.value; 113 return _expression = es.value;
93 } 114 }
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
191 // Otherwise replace it with a temp, which will be assigned once. 212 // Otherwise replace it with a temp, which will be assigned once.
192 var temp = new TemporaryId(variable.displayName); 213 var temp = new TemporaryId(variable.displayName);
193 substitutions[variable] = temp; 214 substitutions[variable] = temp;
194 initializers.add(new VariableInitialization(temp, init)); 215 initializers.add(new VariableInitialization(temp, init));
195 } 216 }
196 }); 217 });
197 218
198 // Interpolate the body. 219 // Interpolate the body.
199 node = _substitute(node, substitutions); 220 node = _substitute(node, substitutions);
200 if (initializers.isNotEmpty) { 221 if (initializers.isNotEmpty) {
222 var first = initializers[0];
201 node = new Block([ 223 node = new Block([
202 new VariableDeclarationList('let', initializers).toStatement(), 224 initializers.length == 1
225 ? first.value.toVariableDeclaration(first.declaration)
226 : new VariableDeclarationList('let', initializers).toStatement(),
203 node 227 node
204 ]); 228 ]);
205 } 229 }
206 return node; 230 return node;
207 } 231 }
208 232
209 /// If we finish with an assignment to an identifier, try to simplify the 233 /// If we finish with an assignment to an identifier, try to simplify the
210 /// block. For example: 234 /// block. For example:
211 /// 235 ///
212 /// ((_) => _.add(1), _.add(2), result = _)([]) 236 /// result = ((_) => _.add(1), _.add(2), _)([])
213 /// 237 ///
214 /// Can be transformed to: 238 /// Can be transformed to:
215 /// 239 ///
216 /// (result = [], result.add(1), result.add(2), result) 240 /// (result = [], result.add(1), result.add(2), result)
217 /// 241 ///
218 /// However we should not simplify in this case because `result` is read: 242 /// However we should not simplify in this case because `result` is read:
219 /// 243 ///
220 /// ((_) => _.addAll(result), _.add(2), result = _)([]) 244 /// result = ((_) => _.addAll(result), _.add(2), _)([])
221 /// 245 ///
222 MetaLet _simplifyAssignment(Identifier left, {bool isDeclaration: false}) { 246 MetaLet _simplifyAssignment(Identifier left,
247 {String op, bool isDeclaration: false}) {
223 // See if the result value is a let* temporary variable. 248 // See if the result value is a let* temporary variable.
224 if (body.last is! MetaLetVariable) return null; 249 var result = body.last;
250 if (result is MetaLetVariable && variables.containsKey(result)) {
251 // For assignments, make sure the identifier isn't used in body, as that
252 // would change the assignment order and be an invalid optimization.
253 if (!isDeclaration && _IdentFinder.foundIn(left.name, body)) return null;
225 254
226 MetaLetVariable result = body.last; 255 var vars = new Map<MetaLetVariable, Expression>.from(variables);
227 if (!variables.containsKey(result)) return null; 256 var value = vars.remove(result);
257 Expression assign;
258 if (isDeclaration) {
259 // Technically, putting one of these in a comma expression is not
260 // legal. However when isDeclaration is true, toStatement will be
261 // called immediately on the MetaLet, which results in legal JS.
262 assign = new VariableDeclarationList(
263 'let', [new VariableInitialization(left, value)]);
264 } else {
265 assign = value.toAssignExpression(left, op);
266 }
228 267
229 // Variables declared can't be used inside their initializer, so make 268 assert(body.isNotEmpty);
230 // sure we don't transform an assignment into an initializer. 269 Binary newBody = new Expression.binary([assign]..addAll(body), ',');
231 // If this already was a declaration, then we know it's legal, so we can 270 newBody = _substitute(newBody, {result: left});
232 // skip the check. 271 return new MetaLet(vars, newBody.commaToExpressionList(),
233 if (!isDeclaration) { 272 statelessResult: statelessResult);
234 var finder = new _IdentFinder(left.name);
235 for (var expr in body) {
236 if (finder.found) break;
237 expr.accept(finder);
238 }
239 // If the identifier was used elsewhere, bail, because we're going to
240 // change the order of when the assignment happens.
241 if (finder.found) return null;
242 } 273 }
243 274 return null;
244 var vars = new Map<MetaLetVariable, Expression>.from(variables);
245 var value = vars.remove(result);
246 Expression assign;
247 if (isDeclaration) {
248 // Technically, putting one of these in a comma expression is not
249 // legal. However when isDeclaration is true, toStatement will be
250 // called immediately on the MetaLet, which results in legal JS.
251 assign = new VariableDeclarationList(
252 'let', [new VariableInitialization(left, value)]);
253 } else {
254 assign = value.toAssignExpression(left);
255 }
256
257 assert(body.isNotEmpty);
258 Binary newBody = new Expression.binary([assign]..addAll(body), ',');
259 newBody = _substitute(newBody, {result: left});
260 return new MetaLet(vars, newBody.commaToExpressionList(),
261 statelessResult: statelessResult);
262 } 275 }
263 } 276 }
264 277
265 /// Similar to [Template.instantiate] but works with free variables. 278 /// Similar to [Template.instantiate] but works with free variables.
266 Node _substitute(Node tree, Map<MetaLetVariable, Expression> substitutions) { 279 Node _substitute(Node tree, Map<MetaLetVariable, Expression> substitutions) {
267 var generator = new InstantiatorGeneratorVisitor(/*forceCopy:*/ false); 280 var generator = new InstantiatorGeneratorVisitor(/*forceCopy:*/ false);
268 var instantiator = generator.compile(tree); 281 var instantiator = generator.compile(tree);
269 var nodes = new List<MetaLetVariable>.from(generator 282 var nodes = new List<MetaLetVariable>.from(generator
270 .analysis.containsInterpolatedNode 283 .analysis.containsInterpolatedNode
271 .where((n) => n is MetaLetVariable)); 284 .where((n) => n is MetaLetVariable));
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 counts[node] = n == null ? 1 : n + 1; 323 counts[node] = n == null ? 1 : n + 1;
311 } 324 }
312 } 325 }
313 } 326 }
314 327
315 class _IdentFinder extends BaseVisitor { 328 class _IdentFinder extends BaseVisitor {
316 final String name; 329 final String name;
317 bool found = false; 330 bool found = false;
318 _IdentFinder(this.name); 331 _IdentFinder(this.name);
319 332
333 static bool foundIn(String name, List<Node> body) {
334 var finder = new _IdentFinder(name);
335 for (var expr in body) {
336 expr.accept(finder);
337 if (finder.found) return true;
338 }
339 return false;
340 }
341
320 @override 342 @override
321 visitIdentifier(Identifier node) { 343 visitIdentifier(Identifier node) {
322 if (node.name == name) found = true; 344 if (node.name == name) found = true;
323 } 345 }
324 346
325 @override 347 @override
326 visitNode(Node node) { 348 visitNode(Node node) {
327 if (!found) super.visitNode(node); 349 if (!found) super.visitNode(node);
328 } 350 }
329 } 351 }
(...skipping 21 matching lines...) Expand all
351 if (!_nestedFunction) hasYield = true; 373 if (!_nestedFunction) hasYield = true;
352 super.visitYield(node); 374 super.visitYield(node);
353 } 375 }
354 376
355 @override 377 @override
356 visitNode(Node node) { 378 visitNode(Node node) {
357 if (hasYield && hasThis) return; // found both, nothing more to do. 379 if (hasYield && hasThis) return; // found both, nothing more to do.
358 super.visitNode(node); 380 super.visitNode(node);
359 } 381 }
360 } 382 }
OLDNEW
« no previous file with comments | « pkg/dev_compiler/lib/src/compiler/code_generator.dart ('k') | pkg/dev_compiler/lib/src/js_ast/nodes.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698