| Index: pkg/compiler/lib/src/ssa/builder.dart
|
| diff --git a/pkg/compiler/lib/src/ssa/builder.dart b/pkg/compiler/lib/src/ssa/builder.dart
|
| index 14615b42ac1e8aa3cc52301d92a71c8c62ac0077..7d088f14978d313aae0b52297de15470e678ad17 100644
|
| --- a/pkg/compiler/lib/src/ssa/builder.dart
|
| +++ b/pkg/compiler/lib/src/ssa/builder.dart
|
| @@ -39,6 +39,7 @@ import '../universe/use.dart' show DynamicUse, StaticUse, TypeUse;
|
| import '../util/util.dart';
|
| import '../world.dart' show ClassWorld;
|
| import 'graph_builder.dart';
|
| +import 'locals_handler.dart';
|
| import 'nodes.dart';
|
| import 'optimize.dart';
|
| import 'types.dart';
|
| @@ -123,632 +124,6 @@ class SsaBuilderTask extends CompilerTask {
|
| }
|
| }
|
|
|
| -/**
|
| - * Keeps track of locals (including parameters and phis) when building. The
|
| - * 'this' reference is treated as parameter and hence handled by this class,
|
| - * too.
|
| - */
|
| -class LocalsHandler {
|
| - /**
|
| - * The values of locals that can be directly accessed (without redirections
|
| - * to boxes or closure-fields).
|
| - *
|
| - * [directLocals] is iterated, so it is "insertion ordered" to make the
|
| - * iteration order a function only of insertions and not a function of
|
| - * e.g. Element hash codes. I'd prefer to use a SortedMap but some elements
|
| - * don't have source locations for [Elements.compareByPosition].
|
| - */
|
| - Map<Local, HInstruction> directLocals = new Map<Local, HInstruction>();
|
| - Map<Local, CapturedVariable> redirectionMapping =
|
| - new Map<Local, CapturedVariable>();
|
| - SsaBuilder builder;
|
| - ClosureClassMap closureData;
|
| - Map<TypeVariableType, TypeVariableLocal> typeVariableLocals =
|
| - new Map<TypeVariableType, TypeVariableLocal>();
|
| - final ExecutableElement executableContext;
|
| -
|
| - /// The class that defines the current type environment or null if no type
|
| - /// variables are in scope.
|
| - ClassElement get contextClass => executableContext.contextClass;
|
| -
|
| - /// The type of the current instance, if concrete.
|
| - ///
|
| - /// This allows for handling fixed type argument in case of inlining. For
|
| - /// instance, checking `'foo'` against `String` instead of `T` in `main`:
|
| - ///
|
| - /// class Foo<T> {
|
| - /// T field;
|
| - /// Foo(this.field);
|
| - /// }
|
| - /// main() {
|
| - /// new Foo<String>('foo');
|
| - /// }
|
| - ///
|
| - /// [instanceType] is not used if it contains type variables, since these
|
| - /// might not be in scope or from the current instance.
|
| - ///
|
| - final InterfaceType instanceType;
|
| -
|
| - SourceInformationBuilder get sourceInformationBuilder {
|
| - return builder.sourceInformationBuilder;
|
| - }
|
| -
|
| - LocalsHandler(
|
| - this.builder, this.executableContext, InterfaceType instanceType)
|
| - : this.instanceType = instanceType == null ||
|
| - instanceType.containsTypeVariables ? null : instanceType;
|
| -
|
| - /// Substituted type variables occurring in [type] into the context of
|
| - /// [contextClass].
|
| - DartType substInContext(DartType type) {
|
| - if (contextClass != null) {
|
| - ClassElement typeContext = Types.getClassContext(type);
|
| - if (typeContext != null) {
|
| - type = type.substByContext(contextClass.asInstanceOf(typeContext));
|
| - }
|
| - }
|
| - if (instanceType != null) {
|
| - type = type.substByContext(instanceType);
|
| - }
|
| - return type;
|
| - }
|
| -
|
| - get typesTask => builder.compiler.typesTask;
|
| -
|
| - /**
|
| - * Creates a new [LocalsHandler] based on [other]. We only need to
|
| - * copy the [directLocals], since the other fields can be shared
|
| - * throughout the AST visit.
|
| - */
|
| - LocalsHandler.from(LocalsHandler other)
|
| - : directLocals = new Map<Local, HInstruction>.from(other.directLocals),
|
| - redirectionMapping = other.redirectionMapping,
|
| - executableContext = other.executableContext,
|
| - instanceType = other.instanceType,
|
| - builder = other.builder,
|
| - closureData = other.closureData;
|
| -
|
| - /**
|
| - * Redirects accesses from element [from] to element [to]. The [to] element
|
| - * must be a boxed variable or a variable that is stored in a closure-field.
|
| - */
|
| - void redirectElement(Local from, CapturedVariable to) {
|
| - assert(redirectionMapping[from] == null);
|
| - redirectionMapping[from] = to;
|
| - assert(isStoredInClosureField(from) || isBoxed(from));
|
| - }
|
| -
|
| - HInstruction createBox() {
|
| - // TODO(floitsch): Clean up this hack. Should we create a box-object by
|
| - // just creating an empty object literal?
|
| - JavaScriptBackend backend = builder.backend;
|
| - HInstruction box = new HForeignCode(
|
| - js.js.parseForeignJS('{}'), backend.nonNullType, <HInstruction>[],
|
| - nativeBehavior: native.NativeBehavior.PURE_ALLOCATION);
|
| - builder.add(box);
|
| - return box;
|
| - }
|
| -
|
| - /**
|
| - * If the scope (function or loop) [node] has captured variables then this
|
| - * method creates a box and sets up the redirections.
|
| - */
|
| - void enterScope(ast.Node node, Element element) {
|
| - // See if any variable in the top-scope of the function is captured. If yes
|
| - // we need to create a box-object.
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - if (scopeData == null) return;
|
| - HInstruction box;
|
| - // The scope has captured variables.
|
| - if (element != null && element.isGenerativeConstructorBody) {
|
| - // The box is passed as a parameter to a generative
|
| - // constructor body.
|
| - JavaScriptBackend backend = builder.backend;
|
| - box = builder.addParameter(scopeData.boxElement, backend.nonNullType);
|
| - } else {
|
| - box = createBox();
|
| - }
|
| - // Add the box to the known locals.
|
| - directLocals[scopeData.boxElement] = box;
|
| - // Make sure that accesses to the boxed locals go into the box. We also
|
| - // need to make sure that parameters are copied into the box if necessary.
|
| - scopeData.forEachCapturedVariable(
|
| - (LocalVariableElement from, BoxFieldElement to) {
|
| - // The [from] can only be a parameter for function-scopes and not
|
| - // loop scopes.
|
| - if (from.isRegularParameter && !element.isGenerativeConstructorBody) {
|
| - // Now that the redirection is set up, the update to the local will
|
| - // write the parameter value into the box.
|
| - // Store the captured parameter in the box. Get the current value
|
| - // before we put the redirection in place.
|
| - // We don't need to update the local for a generative
|
| - // constructor body, because it receives a box that already
|
| - // contains the updates as the last parameter.
|
| - HInstruction instruction = readLocal(from);
|
| - redirectElement(from, to);
|
| - updateLocal(from, instruction);
|
| - } else {
|
| - redirectElement(from, to);
|
| - }
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Replaces the current box with a new box and copies over the given list
|
| - * of elements from the old box into the new box.
|
| - */
|
| - void updateCaptureBox(
|
| - BoxLocal boxElement, List<LocalVariableElement> toBeCopiedElements) {
|
| - // Create a new box and copy over the values from the old box into the
|
| - // new one.
|
| - HInstruction oldBox = readLocal(boxElement);
|
| - HInstruction newBox = createBox();
|
| - for (LocalVariableElement boxedVariable in toBeCopiedElements) {
|
| - // [readLocal] uses the [boxElement] to find its box. By replacing it
|
| - // behind its back we can still get to the old values.
|
| - updateLocal(boxElement, oldBox);
|
| - HInstruction oldValue = readLocal(boxedVariable);
|
| - updateLocal(boxElement, newBox);
|
| - updateLocal(boxedVariable, oldValue);
|
| - }
|
| - updateLocal(boxElement, newBox);
|
| - }
|
| -
|
| - /**
|
| - * Documentation wanted -- johnniwinther
|
| - *
|
| - * Invariant: [function] must be an implementation element.
|
| - */
|
| - void startFunction(AstElement element, ast.Node node) {
|
| - assert(invariant(element, element.isImplementation));
|
| - Compiler compiler = builder.compiler;
|
| - closureData = compiler.closureToClassMapper
|
| - .computeClosureToClassMapping(element.resolvedAst);
|
| -
|
| - if (element is FunctionElement) {
|
| - FunctionElement functionElement = element;
|
| - FunctionSignature params = functionElement.functionSignature;
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - params.orderedForEachParameter((ParameterElement parameterElement) {
|
| - if (element.isGenerativeConstructorBody) {
|
| - if (scopeData != null &&
|
| - scopeData.isCapturedVariable(parameterElement)) {
|
| - // The parameter will be a field in the box passed as the
|
| - // last parameter. So no need to have it.
|
| - return;
|
| - }
|
| - }
|
| - HInstruction parameter = builder.addParameter(parameterElement,
|
| - TypeMaskFactory.inferredTypeForElement(parameterElement, compiler));
|
| - builder.parameters[parameterElement] = parameter;
|
| - directLocals[parameterElement] = parameter;
|
| - });
|
| - }
|
| -
|
| - enterScope(node, element);
|
| -
|
| - // If the freeVariableMapping is not empty, then this function was a
|
| - // nested closure that captures variables. Redirect the captured
|
| - // variables to fields in the closure.
|
| - closureData.forEachFreeVariable((Local from, CapturedVariable to) {
|
| - redirectElement(from, to);
|
| - });
|
| - JavaScriptBackend backend = compiler.backend;
|
| - if (closureData.isClosure) {
|
| - // Inside closure redirect references to itself to [:this:].
|
| - HThis thisInstruction =
|
| - new HThis(closureData.thisLocal, backend.nonNullType);
|
| - builder.graph.thisInstruction = thisInstruction;
|
| - builder.graph.entry.addAtEntry(thisInstruction);
|
| - updateLocal(closureData.closureElement, thisInstruction);
|
| - } else if (element.isInstanceMember) {
|
| - // Once closures have been mapped to classes their instance members might
|
| - // not have any thisElement if the closure was created inside a static
|
| - // context.
|
| - HThis thisInstruction =
|
| - new HThis(closureData.thisLocal, builder.getTypeOfThis());
|
| - builder.graph.thisInstruction = thisInstruction;
|
| - builder.graph.entry.addAtEntry(thisInstruction);
|
| - directLocals[closureData.thisLocal] = thisInstruction;
|
| - }
|
| -
|
| - // If this method is an intercepted method, add the extra
|
| - // parameter to it, that is the actual receiver for intercepted
|
| - // classes, or the same as [:this:] for non-intercepted classes.
|
| - ClassElement cls = element.enclosingClass;
|
| -
|
| - // When the class extends a native class, the instance is pre-constructed
|
| - // and passed to the generative constructor factory function as a parameter.
|
| - // Instead of allocating and initializing the object, the constructor
|
| - // 'upgrades' the native subclass object by initializing the Dart fields.
|
| - bool isNativeUpgradeFactory =
|
| - element.isGenerativeConstructor && backend.isNativeOrExtendsNative(cls);
|
| - if (backend.isInterceptedMethod(element)) {
|
| - bool isInterceptorClass = backend.isInterceptorClass(cls.declaration);
|
| - String name = isInterceptorClass ? 'receiver' : '_';
|
| - SyntheticLocal parameter = new SyntheticLocal(name, executableContext);
|
| - HParameterValue value =
|
| - new HParameterValue(parameter, builder.getTypeOfThis());
|
| - builder.graph.explicitReceiverParameter = value;
|
| - builder.graph.entry.addAfter(directLocals[closureData.thisLocal], value);
|
| - if (builder.lastAddedParameter == null) {
|
| - // If this is the first parameter inserted, make sure it stays first.
|
| - builder.lastAddedParameter = value;
|
| - }
|
| - if (isInterceptorClass) {
|
| - // Only use the extra parameter in intercepted classes.
|
| - directLocals[closureData.thisLocal] = value;
|
| - }
|
| - } else if (isNativeUpgradeFactory) {
|
| - SyntheticLocal parameter =
|
| - new SyntheticLocal('receiver', executableContext);
|
| - // Unlike `this`, receiver is nullable since direct calls to generative
|
| - // constructor call the constructor with `null`.
|
| - ClassWorld classWorld = compiler.world;
|
| - HParameterValue value =
|
| - new HParameterValue(parameter, new TypeMask.exact(cls, classWorld));
|
| - builder.graph.explicitReceiverParameter = value;
|
| - builder.graph.entry.addAtEntry(value);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Returns true if the local can be accessed directly. Boxed variables or
|
| - * captured variables that are stored in the closure-field return [:false:].
|
| - */
|
| - bool isAccessedDirectly(Local local) {
|
| - assert(local != null);
|
| - return !redirectionMapping.containsKey(local) &&
|
| - !closureData.variablesUsedInTryOrGenerator.contains(local);
|
| - }
|
| -
|
| - bool isStoredInClosureField(Local local) {
|
| - assert(local != null);
|
| - if (isAccessedDirectly(local)) return false;
|
| - CapturedVariable redirectTarget = redirectionMapping[local];
|
| - if (redirectTarget == null) return false;
|
| - return redirectTarget is ClosureFieldElement;
|
| - }
|
| -
|
| - bool isBoxed(Local local) {
|
| - if (isAccessedDirectly(local)) return false;
|
| - if (isStoredInClosureField(local)) return false;
|
| - return redirectionMapping.containsKey(local);
|
| - }
|
| -
|
| - bool isUsedInTryOrGenerator(Local local) {
|
| - return closureData.variablesUsedInTryOrGenerator.contains(local);
|
| - }
|
| -
|
| - /**
|
| - * Returns an [HInstruction] for the given element. If the element is
|
| - * boxed or stored in a closure then the method generates code to retrieve
|
| - * the value.
|
| - */
|
| - HInstruction readLocal(Local local, {SourceInformation sourceInformation}) {
|
| - if (isAccessedDirectly(local)) {
|
| - if (directLocals[local] == null) {
|
| - if (local is TypeVariableElement) {
|
| - builder.reporter.internalError(builder.compiler.currentElement,
|
| - "Runtime type information not available for $local.");
|
| - } else {
|
| - builder.reporter.internalError(
|
| - local, "Cannot find value $local in ${directLocals.keys}.");
|
| - }
|
| - }
|
| - HInstruction value = directLocals[local];
|
| - if (sourceInformation != null) {
|
| - value = new HRef(value, sourceInformation);
|
| - builder.add(value);
|
| - }
|
| - return value;
|
| - } else if (isStoredInClosureField(local)) {
|
| - ClosureFieldElement redirect = redirectionMapping[local];
|
| - HInstruction receiver = readLocal(closureData.closureElement);
|
| - TypeMask type = local is BoxLocal
|
| - ? builder.backend.nonNullType
|
| - : builder.getTypeOfCapturedVariable(redirect);
|
| - HInstruction fieldGet = new HFieldGet(redirect, receiver, type);
|
| - builder.add(fieldGet);
|
| - return fieldGet..sourceInformation = sourceInformation;
|
| - } else if (isBoxed(local)) {
|
| - BoxFieldElement redirect = redirectionMapping[local];
|
| - // In the function that declares the captured variable the box is
|
| - // accessed as direct local. Inside the nested closure the box is
|
| - // accessed through a closure-field.
|
| - // Calling [readLocal] makes sure we generate the correct code to get
|
| - // the box.
|
| - HInstruction box = readLocal(redirect.box);
|
| - HInstruction lookup = new HFieldGet(
|
| - redirect, box, builder.getTypeOfCapturedVariable(redirect));
|
| - builder.add(lookup);
|
| - return lookup..sourceInformation = sourceInformation;
|
| - } else {
|
| - assert(isUsedInTryOrGenerator(local));
|
| - HLocalValue localValue = getLocal(local);
|
| - HInstruction instruction = new HLocalGet(
|
| - local, localValue, builder.backend.dynamicType, sourceInformation);
|
| - builder.add(instruction);
|
| - return instruction;
|
| - }
|
| - }
|
| -
|
| - HInstruction readThis() {
|
| - HInstruction res = readLocal(closureData.thisLocal);
|
| - if (res.instructionType == null) {
|
| - res.instructionType = builder.getTypeOfThis();
|
| - }
|
| - return res;
|
| - }
|
| -
|
| - HLocalValue getLocal(Local local, {SourceInformation sourceInformation}) {
|
| - // If the element is a parameter, we already have a
|
| - // HParameterValue for it. We cannot create another one because
|
| - // it could then have another name than the real parameter. And
|
| - // the other one would not know it is just a copy of the real
|
| - // parameter.
|
| - if (local is ParameterElement) {
|
| - assert(invariant(local, builder.parameters.containsKey(local),
|
| - message: "No local value for parameter $local in "
|
| - "${builder.parameters}."));
|
| - return builder.parameters[local];
|
| - }
|
| -
|
| - return builder.activationVariables.putIfAbsent(local, () {
|
| - JavaScriptBackend backend = builder.backend;
|
| - HLocalValue localValue = new HLocalValue(local, backend.nonNullType)
|
| - ..sourceInformation = sourceInformation;
|
| - builder.graph.entry.addAtExit(localValue);
|
| - return localValue;
|
| - });
|
| - }
|
| -
|
| - Local getTypeVariableAsLocal(TypeVariableType type) {
|
| - return typeVariableLocals.putIfAbsent(type, () {
|
| - return new TypeVariableLocal(type, executableContext);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Sets the [element] to [value]. If the element is boxed or stored in a
|
| - * closure then the method generates code to set the value.
|
| - */
|
| - void updateLocal(Local local, HInstruction value,
|
| - {SourceInformation sourceInformation}) {
|
| - if (value is HRef) {
|
| - HRef ref = value;
|
| - value = ref.value;
|
| - }
|
| - assert(!isStoredInClosureField(local));
|
| - if (isAccessedDirectly(local)) {
|
| - directLocals[local] = value;
|
| - } else if (isBoxed(local)) {
|
| - BoxFieldElement redirect = redirectionMapping[local];
|
| - // The box itself could be captured, or be local. A local variable that
|
| - // is captured will be boxed, but the box itself will be a local.
|
| - // Inside the closure the box is stored in a closure-field and cannot
|
| - // be accessed directly.
|
| - HInstruction box = readLocal(redirect.box);
|
| - builder.add(new HFieldSet(redirect, box, value)
|
| - ..sourceInformation = sourceInformation);
|
| - } else {
|
| - assert(isUsedInTryOrGenerator(local));
|
| - HLocalValue localValue = getLocal(local);
|
| - builder.add(new HLocalSet(local, localValue, value)
|
| - ..sourceInformation = sourceInformation);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * This function, startLoop, must be called before visiting any children of
|
| - * the loop. In particular it needs to be called before executing the
|
| - * initializers.
|
| - *
|
| - * The [LocalsHandler] will make the boxes and updates at the right moment.
|
| - * The builder just needs to call [enterLoopBody] and [enterLoopUpdates]
|
| - * (for [ast.For] loops) at the correct places. For phi-handling
|
| - * [beginLoopHeader] and [endLoop] must also be called.
|
| - *
|
| - * The correct place for the box depends on the given loop. In most cases
|
| - * the box will be created when entering the loop-body: while, do-while, and
|
| - * for-in (assuming the call to [:next:] is inside the body) can always be
|
| - * constructed this way.
|
| - *
|
| - * Things are slightly more complicated for [ast.For] loops. If no declared
|
| - * loop variable is boxed then the loop-body approach works here too. If a
|
| - * loop-variable is boxed we need to introduce a new box for the
|
| - * loop-variable before we enter the initializer so that the initializer
|
| - * writes the values into the box. In any case we need to create the box
|
| - * before the condition since the condition could box the variable.
|
| - * Since the first box is created outside the actual loop we have a second
|
| - * location where a box is created: just before the updates. This is
|
| - * necessary since updates are considered to be part of the next iteration
|
| - * (and can again capture variables).
|
| - *
|
| - * For example the following Dart code prints 1 3 -- 3 4.
|
| - *
|
| - * var fs = [];
|
| - * for (var i = 0; i < 3; (f() { fs.add(f); print(i); i++; })()) {
|
| - * i++;
|
| - * }
|
| - * print("--");
|
| - * for (var i = 0; i < 2; i++) fs[i]();
|
| - *
|
| - * We solve this by emitting the following code (only for [ast.For] loops):
|
| - * <Create box> <== move the first box creation outside the loop.
|
| - * <initializer>;
|
| - * loop-entry:
|
| - * if (!<condition>) goto loop-exit;
|
| - * <body>
|
| - * <update box> // create a new box and copy the captured loop-variables.
|
| - * <updates>
|
| - * goto loop-entry;
|
| - * loop-exit:
|
| - */
|
| - void startLoop(ast.Node node) {
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - if (scopeData == null) return;
|
| - if (scopeData.hasBoxedLoopVariables()) {
|
| - // If there are boxed loop variables then we set up the box and
|
| - // redirections already now. This way the initializer can write its
|
| - // values into the box.
|
| - // For other loops the box will be created when entering the body.
|
| - enterScope(node, null);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Create phis at the loop entry for local variables (ready for the values
|
| - * from the back edge). Populate the phis with the current values.
|
| - */
|
| - void beginLoopHeader(HBasicBlock loopEntry) {
|
| - // Create a copy because we modify the map while iterating over it.
|
| - Map<Local, HInstruction> savedDirectLocals =
|
| - new Map<Local, HInstruction>.from(directLocals);
|
| -
|
| - JavaScriptBackend backend = builder.backend;
|
| - // Create phis for all elements in the definitions environment.
|
| - savedDirectLocals.forEach((Local local, HInstruction instruction) {
|
| - if (isAccessedDirectly(local)) {
|
| - // We know 'this' cannot be modified.
|
| - if (local != closureData.thisLocal) {
|
| - HPhi phi =
|
| - new HPhi.singleInput(local, instruction, backend.dynamicType);
|
| - loopEntry.addPhi(phi);
|
| - directLocals[local] = phi;
|
| - } else {
|
| - directLocals[local] = instruction;
|
| - }
|
| - }
|
| - });
|
| - }
|
| -
|
| - void enterLoopBody(ast.Node node) {
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - if (scopeData == null) return;
|
| - // If there are no declared boxed loop variables then we did not create the
|
| - // box before the initializer and we have to create the box now.
|
| - if (!scopeData.hasBoxedLoopVariables()) {
|
| - enterScope(node, null);
|
| - }
|
| - }
|
| -
|
| - void enterLoopUpdates(ast.Node node) {
|
| - // If there are declared boxed loop variables then the updates might have
|
| - // access to the box and we must switch to a new box before executing the
|
| - // updates.
|
| - // In all other cases a new box will be created when entering the body of
|
| - // the next iteration.
|
| - ClosureScope scopeData = closureData.capturingScopes[node];
|
| - if (scopeData == null) return;
|
| - if (scopeData.hasBoxedLoopVariables()) {
|
| - updateCaptureBox(scopeData.boxElement, scopeData.boxedLoopVariables);
|
| - }
|
| - }
|
| -
|
| - /**
|
| - * Goes through the phis created in beginLoopHeader entry and adds the
|
| - * input from the back edge (from the current value of directLocals) to them.
|
| - */
|
| - void endLoop(HBasicBlock loopEntry) {
|
| - // If the loop has an aborting body, we don't update the loop
|
| - // phis.
|
| - if (loopEntry.predecessors.length == 1) return;
|
| - loopEntry.forEachPhi((HPhi phi) {
|
| - Local element = phi.sourceElement;
|
| - HInstruction postLoopDefinition = directLocals[element];
|
| - phi.addInput(postLoopDefinition);
|
| - });
|
| - }
|
| -
|
| - /**
|
| - * Merge [otherLocals] into this locals handler, creating phi-nodes when
|
| - * there is a conflict.
|
| - * If a phi node is necessary, it will use this handler's instruction as the
|
| - * first input, and the otherLocals instruction as the second.
|
| - */
|
| - void mergeWith(LocalsHandler otherLocals, HBasicBlock joinBlock) {
|
| - // If an element is in one map but not the other we can safely
|
| - // ignore it. It means that a variable was declared in the
|
| - // block. Since variable declarations are scoped the declared
|
| - // variable cannot be alive outside the block. Note: this is only
|
| - // true for nodes where we do joins.
|
| - Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
|
| - JavaScriptBackend backend = builder.backend;
|
| - otherLocals.directLocals.forEach((Local local, HInstruction instruction) {
|
| - // We know 'this' cannot be modified.
|
| - if (local == closureData.thisLocal) {
|
| - assert(directLocals[local] == instruction);
|
| - joinedLocals[local] = instruction;
|
| - } else {
|
| - HInstruction mine = directLocals[local];
|
| - if (mine == null) return;
|
| - if (identical(instruction, mine)) {
|
| - joinedLocals[local] = instruction;
|
| - } else {
|
| - HInstruction phi = new HPhi.manyInputs(
|
| - local, <HInstruction>[mine, instruction], backend.dynamicType);
|
| - joinBlock.addPhi(phi);
|
| - joinedLocals[local] = phi;
|
| - }
|
| - }
|
| - });
|
| - directLocals = joinedLocals;
|
| - }
|
| -
|
| - /**
|
| - * When control flow merges, this method can be used to merge several
|
| - * localsHandlers into a new one using phis. The new localsHandler is
|
| - * returned. Unless it is also in the list, the current localsHandler is not
|
| - * used for its values, only for its declared variables. This is a way to
|
| - * exclude local values from the result when they are no longer in scope.
|
| - */
|
| - LocalsHandler mergeMultiple(
|
| - List<LocalsHandler> localsHandlers, HBasicBlock joinBlock) {
|
| - assert(localsHandlers.length > 0);
|
| - if (localsHandlers.length == 1) return localsHandlers[0];
|
| - Map<Local, HInstruction> joinedLocals = new Map<Local, HInstruction>();
|
| - HInstruction thisValue = null;
|
| - JavaScriptBackend backend = builder.backend;
|
| - directLocals.forEach((Local local, HInstruction instruction) {
|
| - if (local != closureData.thisLocal) {
|
| - HPhi phi = new HPhi.noInputs(local, backend.dynamicType);
|
| - joinedLocals[local] = phi;
|
| - joinBlock.addPhi(phi);
|
| - } else {
|
| - // We know that "this" never changes, if it's there.
|
| - // Save it for later. While merging, there is no phi for "this",
|
| - // so we don't have to special case it in the merge loop.
|
| - thisValue = instruction;
|
| - }
|
| - });
|
| - for (LocalsHandler handler in localsHandlers) {
|
| - handler.directLocals.forEach((Local local, HInstruction instruction) {
|
| - HPhi phi = joinedLocals[local];
|
| - if (phi != null) {
|
| - phi.addInput(instruction);
|
| - }
|
| - });
|
| - }
|
| - if (thisValue != null) {
|
| - // If there was a "this" for the scope, add it to the new locals.
|
| - joinedLocals[closureData.thisLocal] = thisValue;
|
| - }
|
| -
|
| - // Remove locals that are not in all handlers.
|
| - directLocals = new Map<Local, HInstruction>();
|
| - joinedLocals.forEach((Local local, HInstruction instruction) {
|
| - if (local != closureData.thisLocal &&
|
| - instruction.inputs.length != localsHandlers.length) {
|
| - joinBlock.removePhi(instruction);
|
| - } else {
|
| - directLocals[local] = instruction;
|
| - }
|
| - });
|
| - return this;
|
| - }
|
| -}
|
| -
|
| // Represents a single break/continue instruction.
|
| class JumpHandlerEntry {
|
| final HJump jumpInstruction;
|
| @@ -1040,20 +415,8 @@ class SsaBuilder extends ast.Visitor
|
|
|
| HInstruction rethrowableException;
|
|
|
| - HParameterValue lastAddedParameter;
|
| -
|
| - Map<ParameterElement, HInstruction> parameters =
|
| - <ParameterElement, HInstruction>{};
|
| -
|
| Map<JumpTarget, JumpHandler> jumpTargets = <JumpTarget, JumpHandler>{};
|
|
|
| - /**
|
| - * Variables stored in the current activation. These variables are
|
| - * being updated in try/catch blocks, and should be
|
| - * accessed indirectly through [HLocalGet] and [HLocalSet].
|
| - */
|
| - Map<Local, HLocalValue> activationVariables = <Local, HLocalValue>{};
|
| -
|
| // We build the Ssa graph by simulating a stack machine.
|
| List<HInstruction> stack = <HInstruction>[];
|
|
|
| @@ -1080,12 +443,12 @@ class SsaBuilder extends ast.Visitor
|
| this.rti = backend.rti {
|
| assert(target.isImplementation);
|
| graph.element = target;
|
| - localsHandler = new LocalsHandler(this, target, null);
|
| sourceElementStack.add(target);
|
| sourceInformationBuilder =
|
| sourceInformationFactory.createBuilderForContext(resolvedAst);
|
| graph.sourceInformation =
|
| sourceInformationBuilder.buildVariableDeclaration();
|
| + localsHandler = new LocalsHandler(this, target, null, compiler);
|
| }
|
|
|
| BackendHelpers get helpers => backend.helpers;
|
| @@ -1488,7 +851,7 @@ class SsaBuilder extends ast.Visitor
|
|
|
| /**
|
| * Return null so it is simple to remove the optional parameters completely
|
| - * from interop methods to match JavaScript semantics for ommitted arguments.
|
| + * from interop methods to match JavaScript semantics for omitted arguments.
|
| */
|
| HInstruction handleConstantForOptionalParameterJsInterop(Element parameter) =>
|
| null;
|
| @@ -1538,38 +901,6 @@ class SsaBuilder extends ast.Visitor
|
| return graph.addConstant(getConstantForNode(node), compiler);
|
| }
|
|
|
| - TypeMask cachedTypeOfThis;
|
| -
|
| - TypeMask getTypeOfThis() {
|
| - TypeMask result = cachedTypeOfThis;
|
| - if (result == null) {
|
| - ThisLocal local = localsHandler.closureData.thisLocal;
|
| - ClassElement cls = local.enclosingClass;
|
| - ClassWorld classWorld = compiler.world;
|
| - if (classWorld.isUsedAsMixin(cls)) {
|
| - // If the enclosing class is used as a mixin, [:this:] can be
|
| - // of the class that mixins the enclosing class. These two
|
| - // classes do not have a subclass relationship, so, for
|
| - // simplicity, we mark the type as an interface type.
|
| - result = new TypeMask.nonNullSubtype(cls.declaration, compiler.world);
|
| - } else {
|
| - result = new TypeMask.nonNullSubclass(cls.declaration, compiler.world);
|
| - }
|
| - cachedTypeOfThis = result;
|
| - }
|
| - return result;
|
| - }
|
| -
|
| - Map<Element, TypeMask> cachedTypesOfCapturedVariables =
|
| - new Map<Element, TypeMask>();
|
| -
|
| - TypeMask getTypeOfCapturedVariable(Element element) {
|
| - assert(element.isField);
|
| - return cachedTypesOfCapturedVariables.putIfAbsent(element, () {
|
| - return TypeMaskFactory.inferredTypeForElement(element, compiler);
|
| - });
|
| - }
|
| -
|
| /**
|
| * Documentation wanted -- johnniwinther
|
| *
|
| @@ -1716,18 +1047,6 @@ class SsaBuilder extends ast.Visitor
|
| return bodyElement;
|
| }
|
|
|
| - HParameterValue addParameter(Entity parameter, TypeMask type) {
|
| - assert(inliningStack.isEmpty);
|
| - HParameterValue result = new HParameterValue(parameter, type);
|
| - if (lastAddedParameter == null) {
|
| - graph.entry.addBefore(graph.entry.first, result);
|
| - } else {
|
| - graph.entry.addAfter(lastAddedParameter, result);
|
| - }
|
| - lastAddedParameter = result;
|
| - return result;
|
| - }
|
| -
|
| /**
|
| * This method sets up the local state of the builder for inlining [function].
|
| * The arguments of the function are inserted into the [localsHandler].
|
| @@ -1742,7 +1061,7 @@ class SsaBuilder extends ast.Visitor
|
| {InterfaceType instanceType}) {
|
| ResolvedAst resolvedAst = function.resolvedAst;
|
| assert(resolvedAst != null);
|
| - localsHandler = new LocalsHandler(this, function, instanceType);
|
| + localsHandler = new LocalsHandler(this, function, instanceType, compiler);
|
| localsHandler.closureData =
|
| compiler.closureToClassMapper.computeClosureToClassMapping(resolvedAst);
|
| returnLocal = new SyntheticLocal("result", function);
|
| @@ -5540,8 +4859,10 @@ class SsaBuilder extends ast.Visitor
|
| // Native behavior effects here are similar to native/behavior.dart.
|
| // The return type is dynamic if we don't trust js-interop type
|
| // declarations.
|
| - nativeBehavior.typesReturned.add(compiler
|
| - .options.trustJSInteropTypeAnnotations ? type : const DynamicType());
|
| + nativeBehavior.typesReturned.add(
|
| + compiler.options.trustJSInteropTypeAnnotations
|
| + ? type
|
| + : const DynamicType());
|
|
|
| // The allocation effects include the declared type if it is native (which
|
| // includes js interop types).
|
|
|