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

Side by Side Diff: pkg/compiler/lib/src/ssa/builder.dart

Issue 2283443002: Make LocalsHandler depend on GraphBuilder instead of SsaBuilder. (Closed)
Patch Set: fix tests Created 4 years, 3 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
« no previous file with comments | « no previous file | pkg/compiler/lib/src/ssa/builder_kernel.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file 1 // Copyright (c) 2012, 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 import 'dart:collection'; 5 import 'dart:collection';
6 6
7 import 'package:js_runtime/shared/embedded_names.dart'; 7 import 'package:js_runtime/shared/embedded_names.dart';
8 8
9 import '../closure.dart'; 9 import '../closure.dart';
10 import '../common.dart'; 10 import '../common.dart';
(...skipping 21 matching lines...) Expand all
32 import '../resolution/tree_elements.dart' show TreeElements; 32 import '../resolution/tree_elements.dart' show TreeElements;
33 import '../tree/tree.dart' as ast; 33 import '../tree/tree.dart' as ast;
34 import '../types/types.dart'; 34 import '../types/types.dart';
35 import '../universe/call_structure.dart' show CallStructure; 35 import '../universe/call_structure.dart' show CallStructure;
36 import '../universe/selector.dart' show Selector; 36 import '../universe/selector.dart' show Selector;
37 import '../universe/side_effects.dart' show SideEffects; 37 import '../universe/side_effects.dart' show SideEffects;
38 import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse; 38 import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse;
39 import '../util/util.dart'; 39 import '../util/util.dart';
40 import '../world.dart' show ClassWorld; 40 import '../world.dart' show ClassWorld;
41 import 'graph_builder.dart'; 41 import 'graph_builder.dart';
42 import 'locals_handler.dart';
42 import 'nodes.dart'; 43 import 'nodes.dart';
43 import 'optimize.dart'; 44 import 'optimize.dart';
44 import 'types.dart'; 45 import 'types.dart';
45 46
46 /// A synthetic local variable only used with the SSA graph. 47 /// A synthetic local variable only used with the SSA graph.
47 /// 48 ///
48 /// For instance used for holding return value of function or the exception of a 49 /// For instance used for holding return value of function or the exception of a
49 /// try-catch statement. 50 /// try-catch statement.
50 class SyntheticLocal extends Local { 51 class SyntheticLocal extends Local {
51 final String name; 52 final String name;
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
116 } 117 }
117 compiler.tracer.traceCompilation(name, work.compilationContext); 118 compiler.tracer.traceCompilation(name, work.compilationContext);
118 compiler.tracer.traceGraph('builder', graph); 119 compiler.tracer.traceGraph('builder', graph);
119 } 120 }
120 return graph; 121 return graph;
121 }); 122 });
122 }); 123 });
123 } 124 }
124 } 125 }
125 126
126 /**
127 * Keeps track of locals (including parameters and phis) when building. The
128 * 'this' reference is treated as parameter and hence handled by this class,
129 * too.
130 */
131 class LocalsHandler {
132 /**
133 * The values of locals that can be directly accessed (without redirections
134 * to boxes or closure-fields).
135 *
136 * [directLocals] is iterated, so it is "insertion ordered" to make the
137 * iteration order a function only of insertions and not a function of
138 * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
139 * don't have source locations for [Elements.compareByPosition].
140 */
141 Map<Local, HInstruction> directLocals = new Map<Local, HInstruction>();
142 Map<Local, CapturedVariable> redirectionMapping =
143 new Map<Local, CapturedVariable>();
144 SsaBuilder builder;
145 ClosureClassMap closureData;
146 Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
147 new Map<TypeVariableType, TypeVariableLocal>();
148 final ExecutableElement executableContext;
149
150 /// The class that defines the current type environment or null if no type
151 /// variables are in scope.
152 ClassElement get contextClass => executableContext.contextClass;
153
154 /// The type of the current instance, if concrete.
155 ///
156 /// This allows for handling fixed type argument in case of inlining. For
157 /// instance, checking `'foo'` against `String` instead of `T` in `main`:
158 ///
159 /// class Foo<T> {
160 /// T field;
161 /// Foo(this.field);
162 /// }
163 /// main() {
164 /// new Foo<String>('foo');
165 /// }
166 ///
167 /// [instanceType] is not used if it contains type variables, since these
168 /// might not be in scope or from the current instance.
169 ///
170 final InterfaceType instanceType;
171
172 SourceInformationBuilder get sourceInformationBuilder {
173 return builder.sourceInformationBuilder;
174 }
175
176 LocalsHandler(
177 this.builder, this.executableContext, InterfaceType instanceType)
178 : this.instanceType = instanceType == null ||
179 instanceType.containsTypeVariables ? null : instanceType;
180
181 /// Substituted type variables occurring in [type] into the context of
182 /// [contextClass].
183 DartType substInContext(DartType type) {
184 if (contextClass != null) {
185 ClassElement typeContext = Types.getClassContext(type);
186 if (typeContext != null) {
187 type = type.substByContext(contextClass.asInstanceOf(typeContext));
188 }
189 }
190 if (instanceType != null) {
191 type = type.substByContext(instanceType);
192 }
193 return type;
194 }
195
196 get typesTask => builder.compiler.typesTask;
197
198 /**
199 * Creates a new [LocalsHandler] based on [other]. We only need to
200 * copy the [directLocals], since the other fields can be shared
201 * throughout the AST visit.
202 */
203 LocalsHandler.from(LocalsHandler other)
204 : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
205 redirectionMapping = other.redirectionMapping,
206 executableContext = other.executableContext,
207 instanceType = other.instanceType,
208 builder = other.builder,
209 closureData = other.closureData;
210
211 /**
212 * Redirects accesses from element [from] to element [to]. The [to] element
213 * must be a boxed variable or a variable that is stored in a closure-field.
214 */
215 void redirectElement(Local from, CapturedVariable to) {
216 assert(redirectionMapping[from] == null);
217 redirectionMapping[from] = to;
218 assert(isStoredInClosureField(from) || isBoxed(from));
219 }
220
221 HInstruction createBox() {
222 // TODO(floitsch): Clean up this hack. Should we create a box-object by
223 // just creating an empty object literal?
224 JavaScriptBackend backend = builder.backend;
225 HInstruction box = new HForeignCode(
226 js.js.parseForeignJS('{}'), backend.nonNullType, <HInstruction>[],
227 nativeBehavior: native.NativeBehavior.PURE_ALLOCATION);
228 builder.add(box);
229 return box;
230 }
231
232 /**
233 * If the scope (function or loop) [node] has captured variables then this
234 * method creates a box and sets up the redirections.
235 */
236 void enterScope(ast.Node node, Element element) {
237 // See if any variable in the top-scope of the function is captured. If yes
238 // we need to create a box-object.
239 ClosureScope scopeData = closureData.capturingScopes[node];
240 if (scopeData == null) return;
241 HInstruction box;
242 // The scope has captured variables.
243 if (element != null && element.isGenerativeConstructorBody) {
244 // The box is passed as a parameter to a generative
245 // constructor body.
246 JavaScriptBackend backend = builder.backend;
247 box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
248 } else {
249 box = createBox();
250 }
251 // Add the box to the known locals.
252 directLocals[scopeData.boxElement] = box;
253 // Make sure that accesses to the boxed locals go into the box. We also
254 // need to make sure that parameters are copied into the box if necessary.
255 scopeData.forEachCapturedVariable(
256 (LocalVariableElement from, BoxFieldElement to) {
257 // The [from] can only be a parameter for function-scopes and not
258 // loop scopes.
259 if (from.isRegularParameter && !element.isGenerativeConstructorBody) {
260 // Now that the redirection is set up, the update to the local will
261 // write the parameter value into the box.
262 // Store the captured parameter in the box. Get the current value
263 // before we put the redirection in place.
264 // We don't need to update the local for a generative
265 // constructor body, because it receives a box that already
266 // contains the updates as the last parameter.
267 HInstruction instruction = readLocal(from);
268 redirectElement(from, to);
269 updateLocal(from, instruction);
270 } else {
271 redirectElement(from, to);
272 }
273 });
274 }
275
276 /**
277 * Replaces the current box with a new box and copies over the given list
278 * of elements from the old box into the new box.
279 */
280 void updateCaptureBox(
281 BoxLocal boxElement, List<LocalVariableElement> toBeCopiedElements) {
282 // Create a new box and copy over the values from the old box into the
283 // new one.
284 HInstruction oldBox = readLocal(boxElement);
285 HInstruction newBox = createBox();
286 for (LocalVariableElement boxedVariable in toBeCopiedElements) {
287 // [readLocal] uses the [boxElement] to find its box. By replacing it
288 // behind its back we can still get to the old values.
289 updateLocal(boxElement, oldBox);
290 HInstruction oldValue = readLocal(boxedVariable);
291 updateLocal(boxElement, newBox);
292 updateLocal(boxedVariable, oldValue);
293 }
294 updateLocal(boxElement, newBox);
295 }
296
297 /**
298 * Documentation wanted -- johnniwinther
299 *
300 * Invariant: [function] must be an implementation element.
301 */
302 void startFunction(AstElement element, ast.Node node) {
303 assert(invariant(element, element.isImplementation));
304 Compiler compiler = builder.compiler;
305 closureData = compiler.closureToClassMapper
306 .computeClosureToClassMapping(element.resolvedAst);
307
308 if (element is FunctionElement) {
309 FunctionElement functionElement = element;
310 FunctionSignature params = functionElement.functionSignature;
311 ClosureScope scopeData = closureData.capturingScopes[node];
312 params.orderedForEachParameter((ParameterElement parameterElement) {
313 if (element.isGenerativeConstructorBody) {
314 if (scopeData != null &&
315 scopeData.isCapturedVariable(parameterElement)) {
316 // The parameter will be a field in the box passed as the
317 // last parameter. So no need to have it.
318 return;
319 }
320 }
321 HInstruction parameter = builder.addParameter(parameterElement,
322 TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
323 builder.parameters[parameterElement] = parameter;
324 directLocals[parameterElement] = parameter;
325 });
326 }
327
328 enterScope(node, element);
329
330 // If the freeVariableMapping is not empty, then this function was a
331 // nested closure that captures variables. Redirect the captured
332 // variables to fields in the closure.
333 closureData.forEachFreeVariable((Local from, CapturedVariable to) {
334 redirectElement(from, to);
335 });
336 JavaScriptBackend backend = compiler.backend;
337 if (closureData.isClosure) {
338 // Inside closure redirect references to itself to [:this:].
339 HThis thisInstruction =
340 new HThis(closureData.thisLocal, backend.nonNullType);
341 builder.graph.thisInstruction = thisInstruction;
342 builder.graph.entry.addAtEntry(thisInstruction);
343 updateLocal(closureData.closureElement, thisInstruction);
344 } else if (element.isInstanceMember) {
345 // Once closures have been mapped to classes their instance members might
346 // not have any thisElement if the closure was created inside a static
347 // context.
348 HThis thisInstruction =
349 new HThis(closureData.thisLocal, builder.getTypeOfThis());
350 builder.graph.thisInstruction = thisInstruction;
351 builder.graph.entry.addAtEntry(thisInstruction);
352 directLocals[closureData.thisLocal] = thisInstruction;
353 }
354
355 // If this method is an intercepted method, add the extra
356 // parameter to it, that is the actual receiver for intercepted
357 // classes, or the same as [:this:] for non-intercepted classes.
358 ClassElement cls = element.enclosingClass;
359
360 // When the class extends a native class, the instance is pre-constructed
361 // and passed to the generative constructor factory function as a parameter.
362 // Instead of allocating and initializing the object, the constructor
363 // 'upgrades' the native subclass object by initializing the Dart fields.
364 bool isNativeUpgradeFactory =
365 element.isGenerativeConstructor && backend.isNativeOrExtendsNative(cls);
366 if (backend.isInterceptedMethod(element)) {
367 bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
368 String name = isInterceptorClass ? 'receiver' : '_';
369 SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
370 HParameterValue value =
371 new HParameterValue(parameter, builder.getTypeOfThis());
372 builder.graph.explicitReceiverParameter = value;
373 builder.graph.entry.addAfter(directLocals[closureData.thisLocal], value);
374 if (builder.lastAddedParameter == null) {
375 // If this is the first parameter inserted, make sure it stays first.
376 builder.lastAddedParameter = value;
377 }
378 if (isInterceptorClass) {
379 // Only use the extra parameter in intercepted classes.
380 directLocals[closureData.thisLocal] = value;
381 }
382 } else if (isNativeUpgradeFactory) {
383 SyntheticLocal parameter =
384 new SyntheticLocal('receiver', executableContext);
385 // Unlike `this`, receiver is nullable since direct calls to generative
386 // constructor call the constructor with `null`.
387 ClassWorld classWorld = compiler.world;
388 HParameterValue value =
389 new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
390 builder.graph.explicitReceiverParameter = value;
391 builder.graph.entry.addAtEntry(value);
392 }
393 }
394
395 /**
396 * Returns true if the local can be accessed directly. Boxed variables or
397 * captured variables that are stored in the closure-field return [:false:].
398 */
399 bool isAccessedDirectly(Local local) {
400 assert(local != null);
401 return !redirectionMapping.containsKey(local) &&
402 !closureData.variablesUsedInTryOrGenerator.contains(local);
403 }
404
405 bool isStoredInClosureField(Local local) {
406 assert(local != null);
407 if (isAccessedDirectly(local)) return false;
408 CapturedVariable redirectTarget = redirectionMapping[local];
409 if (redirectTarget == null) return false;
410 return redirectTarget is ClosureFieldElement;
411 }
412
413 bool isBoxed(Local local) {
414 if (isAccessedDirectly(local)) return false;
415 if (isStoredInClosureField(local)) return false;
416 return redirectionMapping.containsKey(local);
417 }
418
419 bool isUsedInTryOrGenerator(Local local) {
420 return closureData.variablesUsedInTryOrGenerator.contains(local);
421 }
422
423 /**
424 * Returns an [HInstruction] for the given element. If the element is
425 * boxed or stored in a closure then the method generates code to retrieve
426 * the value.
427 */
428 HInstruction readLocal(Local local, {SourceInformation sourceInformation}) {
429 if (isAccessedDirectly(local)) {
430 if (directLocals[local] == null) {
431 if (local is TypeVariableElement) {
432 builder.reporter.internalError(builder.compiler.currentElement,
433 "Runtime type information not available for $local.");
434 } else {
435 builder.reporter.internalError(
436 local, "Cannot find value $local in ${directLocals.keys}.");
437 }
438 }
439 HInstruction value = directLocals[local];
440 if (sourceInformation != null) {
441 value = new HRef(value, sourceInformation);
442 builder.add(value);
443 }
444 return value;
445 } else if (isStoredInClosureField(local)) {
446 ClosureFieldElement redirect = redirectionMapping[local];
447 HInstruction receiver = readLocal(closureData.closureElement);
448 TypeMask type = local is BoxLocal
449 ? builder.backend.nonNullType
450 : builder.getTypeOfCapturedVariable(redirect);
451 HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
452 builder.add(fieldGet);
453 return fieldGet..sourceInformation = sourceInformation;
454 } else if (isBoxed(local)) {
455 BoxFieldElement redirect = redirectionMapping[local];
456 // In the function that declares the captured variable the box is
457 // accessed as direct local. Inside the nested closure the box is
458 // accessed through a closure-field.
459 // Calling [readLocal] makes sure we generate the correct code to get
460 // the box.
461 HInstruction box = readLocal(redirect.box);
462 HInstruction lookup = new HFieldGet(
463 redirect, box, builder.getTypeOfCapturedVariable(redirect));
464 builder.add(lookup);
465 return lookup..sourceInformation = sourceInformation;
466 } else {
467 assert(isUsedInTryOrGenerator(local));
468 HLocalValue localValue = getLocal(local);
469 HInstruction instruction = new HLocalGet(
470 local, localValue, builder.backend.dynamicType, sourceInformation);
471 builder.add(instruction);
472 return instruction;
473 }
474 }
475
476 HInstruction readThis() {
477 HInstruction res = readLocal(closureData.thisLocal);
478 if (res.instructionType == null) {
479 res.instructionType = builder.getTypeOfThis();
480 }
481 return res;
482 }
483
484 HLocalValue getLocal(Local local, {SourceInformation sourceInformation}) {
485 // If the element is a parameter, we already have a
486 // HParameterValue for it. We cannot create another one because
487 // it could then have another name than the real parameter. And
488 // the other one would not know it is just a copy of the real
489 // parameter.
490 if (local is ParameterElement) {
491 assert(invariant(local, builder.parameters.containsKey(local),
492 message: "No local value for parameter $local in "
493 "${builder.parameters}."));
494 return builder.parameters[local];
495 }
496
497 return builder.activationVariables.putIfAbsent(local, () {
498 JavaScriptBackend backend = builder.backend;
499 HLocalValue localValue = new HLocalValue(local, backend.nonNullType)
500 ..sourceInformation = sourceInformation;
501 builder.graph.entry.addAtExit(localValue);
502 return localValue;
503 });
504 }
505
506 Local getTypeVariableAsLocal(TypeVariableType type) {
507 return typeVariableLocals.putIfAbsent(type, () {
508 return new TypeVariableLocal(type, executableContext);
509 });
510 }
511
512 /**
513 * Sets the [element] to [value]. If the element is boxed or stored in a
514 * closure then the method generates code to set the value.
515 */
516 void updateLocal(Local local, HInstruction value,
517 {SourceInformation sourceInformation}) {
518 if (value is HRef) {
519 HRef ref = value;
520 value = ref.value;
521 }
522 assert(!isStoredInClosureField(local));
523 if (isAccessedDirectly(local)) {
524 directLocals[local] = value;
525 } else if (isBoxed(local)) {
526 BoxFieldElement redirect = redirectionMapping[local];
527 // The box itself could be captured, or be local. A local variable that
528 // is captured will be boxed, but the box itself will be a local.
529 // Inside the closure the box is stored in a closure-field and cannot
530 // be accessed directly.
531 HInstruction box = readLocal(redirect.box);
532 builder.add(new HFieldSet(redirect, box, value)
533 ..sourceInformation = sourceInformation);
534 } else {
535 assert(isUsedInTryOrGenerator(local));
536 HLocalValue localValue = getLocal(local);
537 builder.add(new HLocalSet(local, localValue, value)
538 ..sourceInformation = sourceInformation);
539 }
540 }
541
542 /**
543 * This function, startLoop, must be called before visiting any children of
544 * the loop. In particular it needs to be called before executing the
545 * initializers.
546 *
547 * The [LocalsHandler] will make the boxes and updates at the right moment.
548 * The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
549 * (for [ast.For] loops) at the correct places. For phi-handling
550 * [beginLoopHeader] and [endLoop] must also be called.
551 *
552 * The correct place for the box depends on the given loop. In most cases
553 * the box will be created when entering the loop-body: while, do-while, and
554 * for-in (assuming the call to [:next:] is inside the body) can always be
555 * constructed this way.
556 *
557 * Things are slightly more complicated for [ast.For] loops. If no declared
558 * loop variable is boxed then the loop-body approach works here too. If a
559 * loop-variable is boxed we need to introduce a new box for the
560 * loop-variable before we enter the initializer so that the initializer
561 * writes the values into the box. In any case we need to create the box
562 * before the condition since the condition could box the variable.
563 * Since the first box is created outside the actual loop we have a second
564 * location where a box is created: just before the updates. This is
565 * necessary since updates are considered to be part of the next iteration
566 * (and can again capture variables).
567 *
568 * For example the following Dart code prints 1 3 -- 3 4.
569 *
570 * var fs = [];
571 * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
572 * i++;
573 * }
574 * print("--");
575 * for (var i = 0; i < 2; i++) fs[i]();
576 *
577 * We solve this by emitting the following code (only for [ast.For] loops):
578 * <Create box> <== move the first box creation outside the loop.
579 * <initializer>;
580 * loop-entry:
581 * if (!<condition>) goto loop-exit;
582 * <body>
583 * <update box> // create a new box and copy the captured loop-variables.
584 * <updates>
585 * goto loop-entry;
586 * loop-exit:
587 */
588 void startLoop(ast.Node node) {
589 ClosureScope scopeData = closureData.capturingScopes[node];
590 if (scopeData == null) return;
591 if (scopeData.hasBoxedLoopVariables()) {
592 // If there are boxed loop variables then we set up the box and
593 // redirections already now. This way the initializer can write its
594 // values into the box.
595 // For other loops the box will be created when entering the body.
596 enterScope(node, null);
597 }
598 }
599
600 /**
601 * Create phis at the loop entry for local variables (ready for the values
602 * from the back edge). Populate the phis with the current values.
603 */
604 void beginLoopHeader(HBasicBlock loopEntry) {
605 // Create a copy because we modify the map while iterating over it.
606 Map<Local, HInstruction> savedDirectLocals =
607 new Map<Local, HInstruction>.from(directLocals);
608
609 JavaScriptBackend backend = builder.backend;
610 // Create phis for all elements in the definitions environment.
611 savedDirectLocals.forEach((Local local, HInstruction instruction) {
612 if (isAccessedDirectly(local)) {
613 // We know 'this' cannot be modified.
614 if (local != closureData.thisLocal) {
615 HPhi phi =
616 new HPhi.singleInput(local, instruction, backend.dynamicType);
617 loopEntry.addPhi(phi);
618 directLocals[local] = phi;
619 } else {
620 directLocals[local] = instruction;
621 }
622 }
623 });
624 }
625
626 void enterLoopBody(ast.Node node) {
627 ClosureScope scopeData = closureData.capturingScopes[node];
628 if (scopeData == null) return;
629 // If there are no declared boxed loop variables then we did not create the
630 // box before the initializer and we have to create the box now.
631 if (!scopeData.hasBoxedLoopVariables()) {
632 enterScope(node, null);
633 }
634 }
635
636 void enterLoopUpdates(ast.Node node) {
637 // If there are declared boxed loop variables then the updates might have
638 // access to the box and we must switch to a new box before executing the
639 // updates.
640 // In all other cases a new box will be created when entering the body of
641 // the next iteration.
642 ClosureScope scopeData = closureData.capturingScopes[node];
643 if (scopeData == null) return;
644 if (scopeData.hasBoxedLoopVariables()) {
645 updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
646 }
647 }
648
649 /**
650 * Goes through the phis created in beginLoopHeader entry and adds the
651 * input from the back edge (from the current value of directLocals) to them.
652 */
653 void endLoop(HBasicBlock loopEntry) {
654 // If the loop has an aborting body, we don't update the loop
655 // phis.
656 if (loopEntry.predecessors.length == 1) return;
657 loopEntry.forEachPhi((HPhi phi) {
658 Local element = phi.sourceElement;
659 HInstruction postLoopDefinition = directLocals[element];
660 phi.addInput(postLoopDefinition);
661 });
662 }
663
664 /**
665 * Merge [otherLocals] into this locals handler, creating phi-nodes when
666 * there is a conflict.
667 * If a phi node is necessary, it will use this handler's instruction as the
668 * first input, and the otherLocals instruction as the second.
669 */
670 void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
671 // If an element is in one map but not the other we can safely
672 // ignore it. It means that a variable was declared in the
673 // block. Since variable declarations are scoped the declared
674 // variable cannot be alive outside the block. Note: this is only
675 // true for nodes where we do joins.
676 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
677 JavaScriptBackend backend = builder.backend;
678 otherLocals.directLocals.forEach((Local local, HInstruction instruction) {
679 // We know 'this' cannot be modified.
680 if (local == closureData.thisLocal) {
681 assert(directLocals[local] == instruction);
682 joinedLocals[local] = instruction;
683 } else {
684 HInstruction mine = directLocals[local];
685 if (mine == null) return;
686 if (identical(instruction, mine)) {
687 joinedLocals[local] = instruction;
688 } else {
689 HInstruction phi = new HPhi.manyInputs(
690 local, <HInstruction>[mine, instruction], backend.dynamicType);
691 joinBlock.addPhi(phi);
692 joinedLocals[local] = phi;
693 }
694 }
695 });
696 directLocals = joinedLocals;
697 }
698
699 /**
700 * When control flow merges, this method can be used to merge several
701 * localsHandlers into a new one using phis. The new localsHandler is
702 * returned. Unless it is also in the list, the current localsHandler is not
703 * used for its values, only for its declared variables. This is a way to
704 * exclude local values from the result when they are no longer in scope.
705 */
706 LocalsHandler mergeMultiple(
707 List<LocalsHandler> localsHandlers, HBasicBlock joinBlock) {
708 assert(localsHandlers.length > 0);
709 if (localsHandlers.length == 1) return localsHandlers[0];
710 Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
711 HInstruction thisValue = null;
712 JavaScriptBackend backend = builder.backend;
713 directLocals.forEach((Local local, HInstruction instruction) {
714 if (local != closureData.thisLocal) {
715 HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
716 joinedLocals[local] = phi;
717 joinBlock.addPhi(phi);
718 } else {
719 // We know that "this" never changes, if it's there.
720 // Save it for later. While merging, there is no phi for "this",
721 // so we don't have to special case it in the merge loop.
722 thisValue = instruction;
723 }
724 });
725 for (LocalsHandler handler in localsHandlers) {
726 handler.directLocals.forEach((Local local, HInstruction instruction) {
727 HPhi phi = joinedLocals[local];
728 if (phi != null) {
729 phi.addInput(instruction);
730 }
731 });
732 }
733 if (thisValue != null) {
734 // If there was a "this" for the scope, add it to the new locals.
735 joinedLocals[closureData.thisLocal] = thisValue;
736 }
737
738 // Remove locals that are not in all handlers.
739 directLocals = new Map<Local, HInstruction>();
740 joinedLocals.forEach((Local local, HInstruction instruction) {
741 if (local != closureData.thisLocal &&
742 instruction.inputs.length != localsHandlers.length) {
743 joinBlock.removePhi(instruction);
744 } else {
745 directLocals[local] = instruction;
746 }
747 });
748 return this;
749 }
750 }
751
752 // Represents a single break/continue instruction. 127 // Represents a single break/continue instruction.
753 class JumpHandlerEntry { 128 class JumpHandlerEntry {
754 final HJump jumpInstruction; 129 final HJump jumpInstruction;
755 final LocalsHandler locals; 130 final LocalsHandler locals;
756 bool isBreak() => jumpInstruction is HBreak; 131 bool isBreak() => jumpInstruction is HBreak;
757 bool isContinue() => jumpInstruction is HContinue; 132 bool isContinue() => jumpInstruction is HContinue;
758 JumpHandlerEntry(this.jumpInstruction, this.locals); 133 JumpHandlerEntry(this.jumpInstruction, this.locals);
759 } 134 }
760 135
761 abstract class JumpHandler { 136 abstract class JumpHandler {
(...skipping 271 matching lines...) Expand 10 before | Expand all | Expand 10 after
1033 /** 408 /**
1034 * This stack contains declaration elements of the functions being built 409 * This stack contains declaration elements of the functions being built
1035 * or inlined by this builder. 410 * or inlined by this builder.
1036 */ 411 */
1037 final List<Element> sourceElementStack = <Element>[]; 412 final List<Element> sourceElementStack = <Element>[];
1038 413
1039 LocalsHandler localsHandler; 414 LocalsHandler localsHandler;
1040 415
1041 HInstruction rethrowableException; 416 HInstruction rethrowableException;
1042 417
1043 HParameterValue lastAddedParameter;
1044
1045 Map<ParameterElement, HInstruction> parameters =
1046 <ParameterElement, HInstruction>{};
1047
1048 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{}; 418 Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
1049 419
1050 /**
1051 * Variables stored in the current activation. These variables are
1052 * being updated in try/catch blocks, and should be
1053 * accessed indirectly through [HLocalGet] and [HLocalSet].
1054 */
1055 Map<Local, HLocalValue> activationVariables = <Local, HLocalValue>{};
1056
1057 // We build the Ssa graph by simulating a stack machine. 420 // We build the Ssa graph by simulating a stack machine.
1058 List<HInstruction> stack = <HInstruction>[]; 421 List<HInstruction> stack = <HInstruction>[];
1059 422
1060 /// Returns `true` if the current element is an `async` function. 423 /// Returns `true` if the current element is an `async` function.
1061 bool get isBuildingAsyncFunction { 424 bool get isBuildingAsyncFunction {
1062 Element element = sourceElement; 425 Element element = sourceElement;
1063 return (element is FunctionElement && 426 return (element is FunctionElement &&
1064 element.asyncMarker == AsyncMarker.ASYNC); 427 element.asyncMarker == AsyncMarker.ASYNC);
1065 } 428 }
1066 429
1067 // TODO(sigmund): make most args optional 430 // TODO(sigmund): make most args optional
1068 SsaBuilder( 431 SsaBuilder(
1069 this.target, 432 this.target,
1070 this.resolvedAst, 433 this.resolvedAst,
1071 this.context, 434 this.context,
1072 this.registry, 435 this.registry,
1073 JavaScriptBackend backend, 436 JavaScriptBackend backend,
1074 this.nativeEmitter, 437 this.nativeEmitter,
1075 SourceInformationStrategy sourceInformationFactory) 438 SourceInformationStrategy sourceInformationFactory)
1076 : this.compiler = backend.compiler, 439 : this.compiler = backend.compiler,
1077 this.infoReporter = backend.compiler.dumpInfoTask, 440 this.infoReporter = backend.compiler.dumpInfoTask,
1078 this.backend = backend, 441 this.backend = backend,
1079 this.constantSystem = backend.constantSystem, 442 this.constantSystem = backend.constantSystem,
1080 this.rti = backend.rti { 443 this.rti = backend.rti {
1081 assert(target.isImplementation); 444 assert(target.isImplementation);
1082 graph.element = target; 445 graph.element = target;
1083 localsHandler = new LocalsHandler(this, target, null);
1084 sourceElementStack.add(target); 446 sourceElementStack.add(target);
1085 sourceInformationBuilder = 447 sourceInformationBuilder =
1086 sourceInformationFactory.createBuilderForContext(resolvedAst); 448 sourceInformationFactory.createBuilderForContext(resolvedAst);
1087 graph.sourceInformation = 449 graph.sourceInformation =
1088 sourceInformationBuilder.buildVariableDeclaration(); 450 sourceInformationBuilder.buildVariableDeclaration();
451 localsHandler = new LocalsHandler(this, target, null, compiler);
1089 } 452 }
1090 453
1091 BackendHelpers get helpers => backend.helpers; 454 BackendHelpers get helpers => backend.helpers;
1092 455
1093 RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder; 456 RuntimeTypesEncoder get rtiEncoder => backend.rtiEncoder;
1094 457
1095 DiagnosticReporter get reporter => compiler.reporter; 458 DiagnosticReporter get reporter => compiler.reporter;
1096 459
1097 CoreClasses get coreClasses => compiler.coreClasses; 460 CoreClasses get coreClasses => compiler.coreClasses;
1098 461
(...skipping 382 matching lines...) Expand 10 before | Expand all | Expand 10 after
1481 sourceElementStack.add(element.declaration); 844 sourceElementStack.add(element.declaration);
1482 var result = f(); 845 var result = f();
1483 sourceInformationBuilder = oldSourceInformationBuilder; 846 sourceInformationBuilder = oldSourceInformationBuilder;
1484 sourceElementStack.removeLast(); 847 sourceElementStack.removeLast();
1485 return result; 848 return result;
1486 }); 849 });
1487 } 850 }
1488 851
1489 /** 852 /**
1490 * Return null so it is simple to remove the optional parameters completely 853 * Return null so it is simple to remove the optional parameters completely
1491 * from interop methods to match JavaScript semantics for ommitted arguments. 854 * from interop methods to match JavaScript semantics for omitted arguments.
1492 */ 855 */
1493 HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) => 856 HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) =>
1494 null; 857 null;
1495 858
1496 HInstruction handleConstantForOptionalParameter(ParameterElement parameter) { 859 HInstruction handleConstantForOptionalParameter(ParameterElement parameter) {
1497 ConstantValue constantValue = 860 ConstantValue constantValue =
1498 backend.constants.getConstantValue(parameter.constant); 861 backend.constants.getConstantValue(parameter.constant);
1499 assert(invariant(parameter, constantValue != null, 862 assert(invariant(parameter, constantValue != null,
1500 message: 'No constant computed for $parameter')); 863 message: 'No constant computed for $parameter'));
1501 return graph.addConstant(constantValue, compiler); 864 return graph.addConstant(constantValue, compiler);
(...skipping 29 matching lines...) Expand all
1531 backend.constants.getConstantValueForNode(node, elements); 894 backend.constants.getConstantValueForNode(node, elements);
1532 assert(invariant(node, constantValue != null, 895 assert(invariant(node, constantValue != null,
1533 message: 'No constant computed for $node')); 896 message: 'No constant computed for $node'));
1534 return constantValue; 897 return constantValue;
1535 } 898 }
1536 899
1537 HInstruction addConstant(ast.Node node) { 900 HInstruction addConstant(ast.Node node) {
1538 return graph.addConstant(getConstantForNode(node), compiler); 901 return graph.addConstant(getConstantForNode(node), compiler);
1539 } 902 }
1540 903
1541 TypeMask cachedTypeOfThis;
1542
1543 TypeMask getTypeOfThis() {
1544 TypeMask result = cachedTypeOfThis;
1545 if (result == null) {
1546 ThisLocal local = localsHandler.closureData.thisLocal;
1547 ClassElement cls = local.enclosingClass;
1548 ClassWorld classWorld = compiler.world;
1549 if (classWorld.isUsedAsMixin(cls)) {
1550 // If the enclosing class is used as a mixin, [:this:] can be
1551 // of the class that mixins the enclosing class. These two
1552 // classes do not have a subclass relationship, so, for
1553 // simplicity, we mark the type as an interface type.
1554 result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
1555 } else {
1556 result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
1557 }
1558 cachedTypeOfThis = result;
1559 }
1560 return result;
1561 }
1562
1563 Map<Element, TypeMask> cachedTypesOfCapturedVariables =
1564 new Map<Element, TypeMask>();
1565
1566 TypeMask getTypeOfCapturedVariable(Element element) {
1567 assert(element.isField);
1568 return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
1569 return TypeMaskFactory.inferredTypeForElement(element, compiler);
1570 });
1571 }
1572
1573 /** 904 /**
1574 * Documentation wanted -- johnniwinther 905 * Documentation wanted -- johnniwinther
1575 * 906 *
1576 * Invariant: [functionElement] must be an implementation element. 907 * Invariant: [functionElement] must be an implementation element.
1577 */ 908 */
1578 HGraph buildMethod(FunctionElement functionElement) { 909 HGraph buildMethod(FunctionElement functionElement) {
1579 assert(invariant(functionElement, functionElement.isImplementation)); 910 assert(invariant(functionElement, functionElement.isImplementation));
1580 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement); 911 graph.calledInLoop = compiler.world.isCalledInLoop(functionElement);
1581 ast.FunctionExpression function = resolvedAst.node; 912 ast.FunctionExpression function = resolvedAst.node;
1582 assert(function != null); 913 assert(function != null);
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after
1709 ConstructorBodyElementX origin = new ConstructorBodyElementX( 1040 ConstructorBodyElementX origin = new ConstructorBodyElementX(
1710 constructorResolvedAst, constructor.origin); 1041 constructorResolvedAst, constructor.origin);
1711 origin.applyPatch(patch); 1042 origin.applyPatch(patch);
1712 classElement.origin.addBackendMember(bodyElement.origin); 1043 classElement.origin.addBackendMember(bodyElement.origin);
1713 } 1044 }
1714 } 1045 }
1715 assert(bodyElement.isGenerativeConstructorBody); 1046 assert(bodyElement.isGenerativeConstructorBody);
1716 return bodyElement; 1047 return bodyElement;
1717 } 1048 }
1718 1049
1719 HParameterValue addParameter(Entity parameter, TypeMask type) {
1720 assert(inliningStack.isEmpty);
1721 HParameterValue result = new HParameterValue(parameter, type);
1722 if (lastAddedParameter == null) {
1723 graph.entry.addBefore(graph.entry.first, result);
1724 } else {
1725 graph.entry.addAfter(lastAddedParameter, result);
1726 }
1727 lastAddedParameter = result;
1728 return result;
1729 }
1730
1731 /** 1050 /**
1732 * This method sets up the local state of the builder for inlining [function]. 1051 * This method sets up the local state of the builder for inlining [function].
1733 * The arguments of the function are inserted into the [localsHandler]. 1052 * The arguments of the function are inserted into the [localsHandler].
1734 * 1053 *
1735 * When inlining a function, [:return:] statements are not emitted as 1054 * When inlining a function, [:return:] statements are not emitted as
1736 * [HReturn] instructions. Instead, the value of a synthetic element is 1055 * [HReturn] instructions. Instead, the value of a synthetic element is
1737 * updated in the [localsHandler]. This function creates such an element and 1056 * updated in the [localsHandler]. This function creates such an element and
1738 * stores it in the [returnLocal] field. 1057 * stores it in the [returnLocal] field.
1739 */ 1058 */
1740 void setupStateForInlining( 1059 void setupStateForInlining(
1741 FunctionElement function, List<HInstruction> compiledArguments, 1060 FunctionElement function, List<HInstruction> compiledArguments,
1742 {InterfaceType instanceType}) { 1061 {InterfaceType instanceType}) {
1743 ResolvedAst resolvedAst = function.resolvedAst; 1062 ResolvedAst resolvedAst = function.resolvedAst;
1744 assert(resolvedAst != null); 1063 assert(resolvedAst != null);
1745 localsHandler = new LocalsHandler(this, function, instanceType); 1064 localsHandler = new LocalsHandler(this, function, instanceType, compiler);
1746 localsHandler.closureData = 1065 localsHandler.closureData =
1747 compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst); 1066 compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst);
1748 returnLocal = new SyntheticLocal("result", function); 1067 returnLocal = new SyntheticLocal("result", function);
1749 localsHandler.updateLocal(returnLocal, graph.addConstantNull(compiler)); 1068 localsHandler.updateLocal(returnLocal, graph.addConstantNull(compiler));
1750 1069
1751 inTryStatement = false; // TODO(lry): why? Document. 1070 inTryStatement = false; // TODO(lry): why? Document.
1752 1071
1753 int argumentIndex = 0; 1072 int argumentIndex = 0;
1754 if (function.isInstanceMember) { 1073 if (function.isInstanceMember) {
1755 localsHandler.updateLocal(localsHandler.closureData.thisLocal, 1074 localsHandler.updateLocal(localsHandler.closureData.thisLocal,
(...skipping 3777 matching lines...) Expand 10 before | Expand all | Expand 10 after
5533 4852
5534 var nativeBehavior = new native.NativeBehavior() 4853 var nativeBehavior = new native.NativeBehavior()
5535 ..sideEffects.setAllSideEffects(); 4854 ..sideEffects.setAllSideEffects();
5536 4855
5537 DartType type = element.isConstructor 4856 DartType type = element.isConstructor
5538 ? element.enclosingClass.thisType 4857 ? element.enclosingClass.thisType
5539 : element.type.returnType; 4858 : element.type.returnType;
5540 // Native behavior effects here are similar to native/behavior.dart. 4859 // Native behavior effects here are similar to native/behavior.dart.
5541 // The return type is dynamic if we don't trust js-interop type 4860 // The return type is dynamic if we don't trust js-interop type
5542 // declarations. 4861 // declarations.
5543 nativeBehavior.typesReturned.add(compiler 4862 nativeBehavior.typesReturned.add(
5544 .options.trustJSInteropTypeAnnotations ? type : const DynamicType()); 4863 compiler.options.trustJSInteropTypeAnnotations
4864 ? type
4865 : const DynamicType());
5545 4866
5546 // The allocation effects include the declared type if it is native (which 4867 // The allocation effects include the declared type if it is native (which
5547 // includes js interop types). 4868 // includes js interop types).
5548 if (type.element != null && backend.isNative(type.element)) { 4869 if (type.element != null && backend.isNative(type.element)) {
5549 nativeBehavior.typesInstantiated.add(type); 4870 nativeBehavior.typesInstantiated.add(type);
5550 } 4871 }
5551 4872
5552 // It also includes any other JS interop type if we don't trust the 4873 // It also includes any other JS interop type if we don't trust the
5553 // annotation or if is declared too broad. 4874 // annotation or if is declared too broad.
5554 if (!compiler.options.trustJSInteropTypeAnnotations || 4875 if (!compiler.options.trustJSInteropTypeAnnotations ||
(...skipping 2968 matching lines...) Expand 10 before | Expand all | Expand 10 after
8523 const _LoopTypeVisitor(); 7844 const _LoopTypeVisitor();
8524 int visitNode(ast.Node node) => HLoopBlockInformation.NOT_A_LOOP; 7845 int visitNode(ast.Node node) => HLoopBlockInformation.NOT_A_LOOP;
8525 int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP; 7846 int visitWhile(ast.While node) => HLoopBlockInformation.WHILE_LOOP;
8526 int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP; 7847 int visitFor(ast.For node) => HLoopBlockInformation.FOR_LOOP;
8527 int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP; 7848 int visitDoWhile(ast.DoWhile node) => HLoopBlockInformation.DO_WHILE_LOOP;
8528 int visitAsyncForIn(ast.AsyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; 7849 int visitAsyncForIn(ast.AsyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP;
8529 int visitSyncForIn(ast.SyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP; 7850 int visitSyncForIn(ast.SyncForIn node) => HLoopBlockInformation.FOR_IN_LOOP;
8530 int visitSwitchStatement(ast.SwitchStatement node) => 7851 int visitSwitchStatement(ast.SwitchStatement node) =>
8531 HLoopBlockInformation.SWITCH_CONTINUE_LOOP; 7852 HLoopBlockInformation.SWITCH_CONTINUE_LOOP;
8532 } 7853 }
OLDNEW
« no previous file with comments | « no previous file | pkg/compiler/lib/src/ssa/builder_kernel.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698