Chromium Code Reviews| Index: pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| diff --git a/pkg/compiler/lib/src/cps_ir/type_propagation.dart b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| index 9d1a33523cc5b5146afa8fc69e2fa454f71e3ec1..c0eef51dbe2e2129dc4fa33e6c5614e4a8413931 100644 |
| --- a/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| +++ b/pkg/compiler/lib/src/cps_ir/type_propagation.dart |
| @@ -613,14 +613,11 @@ class TypePropagator extends Pass { |
| @override |
| void rewrite(FunctionDefinition root) { |
| - Map<Expression, ConstantValue> replacements = <Expression, ConstantValue>{}; |
| - |
| // Analyze. In this phase, the entire term is analyzed for reachability |
| // and the abstract value of each expression. |
| TypePropagationVisitor analyzer = new TypePropagationVisitor( |
| _lattice, |
| _values, |
| - replacements, |
| _internalError); |
| analyzer.analyze(root); |
| @@ -633,7 +630,6 @@ class TypePropagator extends Pass { |
| _functionCompiler, |
| _lattice, |
| analyzer, |
| - replacements, |
| _internalError); |
| transformer.transform(root); |
| } |
| @@ -660,7 +656,6 @@ final Map<String, BuiltinOperator> NumBinaryBuiltins = |
| */ |
| class TransformingVisitor extends DeepRecursiveVisitor { |
| final TypePropagationVisitor analyzer; |
| - final Map<Expression, ConstantValue> replacements; |
| final ConstantPropagationLattice lattice; |
| final dart2js.Compiler compiler; |
| final CpsFunctionCompiler functionCompiler; |
| @@ -680,7 +675,6 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| this.functionCompiler, |
| this.lattice, |
| this.analyzer, |
| - this.replacements, |
| this.internalError); |
| void transform(FunctionDefinition root) { |
| @@ -726,32 +720,63 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| value.isConstant) { |
| // If the value is a known constant, compile it as a constant. |
| Constant newPrim = makeConstantPrimitive(value.constant); |
| + newPrim.hint = node.primitive.hint; |
| newPrim.substituteFor(node.primitive); |
| RemovalVisitor.remove(node.primitive); |
| node.primitive = newPrim; |
| newPrim.parent = node; |
| + push(node.body); |
| + return; |
| + } |
| + var replacement = visit(node.primitive); |
| + if (replacement is CpsFragment) { |
| + reanalyzeFragment(replacement); |
| + replacement.insertBelow(node); |
| + push(node.body); // Get the body before removing the node. |
| + node.primitive.destroy(); |
| + node.remove(); |
| + return; |
| + } |
| + if (replacement is Primitive) { |
| + node.primitive.redefineAs(replacement); |
| + reanalyze(replacement); |
| } else { |
| - Primitive newPrim = visit(node.primitive); |
| - if (newPrim != null) { |
| - newPrim.substituteFor(node.primitive); |
| - RemovalVisitor.remove(node.primitive); |
| - node.primitive = newPrim; |
| - newPrim.parent = node; |
| - reanalyze(newPrim); |
| - } |
| - if (node.primitive.hasNoUses && node.primitive.isSafeForElimination) { |
| - // Remove unused primitives before entering the body. |
| - // This would also be done by shrinking reductions, but usage analyses |
| - // such as isAlwaysBoolified are more precise without the dead uses, so |
| - // we prefer to remove them early. |
| - RemovalVisitor.remove(node.primitive); |
| - node.parent.body = node.body; |
| - node.body.parent = node.parent; |
| - } |
| + assert(replacement == null); |
| + } |
| + if (node.primitive.hasNoUses && node.primitive.isSafeForElimination) { |
| + // Remove unused primitives before entering the body. |
| + // This would also be done by shrinking reductions, but some usage |
| + // analyses are more precise without the dead uses. |
| + push(node.body); |
| + node.primitive.destroy(); |
| + node.remove(); |
| + return; |
| + } |
| + if (isAlwaysThrowing(node.primitive)) { |
| + replaceSubtree(node.body, new Unreachable()); |
| + return; |
| } |
| push(node.body); |
| } |
| + bool usedToBeCallExpression(Primitive prim) { |
| + return prim is InvokeMethod || |
| + prim is InvokeStatic || |
| + prim is InvokeConstructor || |
| + prim is InvokeMethodDirectly || |
| + prim is TypeCast || |
| + prim is ForeignCode || |
| + prim is GetLazyStatic; |
| + } |
| + |
| + bool isAlwaysThrowing(Primitive prim) { |
| + // TODO(asgerf): Generalize this to prim.hasValue && type.isReallyEmpty. |
| + // But for now, just reproduce how this worked before. |
| + if (!usedToBeCallExpression(prim)) return false; |
| + if (prim.type == null) throw 'Missing type for $prim'; |
| + return prim.type.isEmpty && !prim.type.isNullable; |
| + } |
| + |
| void visitContinuation(Continuation node) { |
| if (node.isReturnContinuation) return; |
| if (!analyzer.reachableContinuations.contains(node)) { |
| @@ -771,6 +796,22 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| analyzer.reanalyzeSubtree(node); |
| } |
| + /// Sets parent pointers and computes types for the given fragment. |
| + void reanalyzeFragment(CpsFragment code) { |
| + if (code.isEmpty) return; |
| + if (code.isOpen) { |
| + // Temporarily close the fragment while analyzing it. |
| + // TODO(asgerf): Perhaps the analyzer should just cope with missing nodes. |
| + InteriorNode context = code.context; |
| + code.put(new Unreachable()); |
| + reanalyze(code.root); |
| + code.context = context; |
| + context.body = null; |
| + } else { |
| + reanalyze(code.root); |
| + } |
| + } |
| + |
| /// Removes the entire subtree of [node] and inserts [replacement]. |
| /// |
| /// By default, all references in the [node] subtree are unlinked, and parent |
| @@ -818,16 +859,6 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| node.parent = context; |
| } |
| - /// Binds [prim] before [node]. |
| - void insertLetPrim(Expression node, Primitive prim) { |
| - InteriorNode parent = node.parent; |
| - LetPrim let = new LetPrim(prim); |
| - parent.body = let; |
| - let.body = node; |
| - node.parent = let; |
| - let.parent = parent; |
| - } |
| - |
| /// Make a constant primitive for [constant] and set its entry in [values]. |
| Constant makeConstantPrimitive(ConstantValue constant) { |
| Constant primitive = new Constant(constant); |
| @@ -967,32 +998,30 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /// Replaces [node] with a more specialized instruction, if possible. |
| /// |
| /// Returns `true` if the node was replaced. |
| - bool specializeOperatorCall(InvokeMethod node) { |
| - Continuation cont = node.continuation.definition; |
| - bool replaceWithPrimitive(Primitive prim) { |
| - LetPrim let = makeLetPrimInvoke(prim, cont); |
| - replaceSubtree(node, let); |
| - push(let); |
| - return true; // So returning early is more convenient. |
| - } |
| - bool replaceWithBinary(BuiltinOperator operator, |
| - Primitive left, |
| - Primitive right) { |
| - return replaceWithPrimitive( |
| - new ApplyBuiltinOperator( |
| - operator, <Primitive>[left, right], node.sourceInformation)); |
| - } |
| - bool replaceWithUnary(BuiltinOperator operator, Primitive argument) { |
| - return replaceWithPrimitive( |
| - new ApplyBuiltinOperator( |
| - operator, <Primitive>[argument], node.sourceInformation)); |
| + specializeOperatorCall(InvokeMethod node) { |
| + replaceWithBinary(BuiltinOperator operator, |
| + Primitive left, |
| + Primitive right, |
| + {bool reprocess: false}) { |
| + Primitive newPrim = new ApplyBuiltinOperator( |
| + operator, <Primitive>[left, right], node.sourceInformation); |
| + if (!reprocess) return newPrim; |
| + // CpsFragments are always reprocessed when returned. |
| + // TODO(asgerf): Kind of a hack. Is there a nicer way? |
| + newPrim.substituteFor(node); |
| + newPrim.hint = node.hint; |
| + return new CpsFragment()..letPrim(newPrim); |
| + } |
| + replaceWithUnary(BuiltinOperator operator, Primitive argument) { |
| + return new ApplyBuiltinOperator( |
| + operator, <Primitive>[argument], node.sourceInformation); |
| } |
| bool trustPrimitives = compiler.trustPrimitives; |
| if (node.selector.isOperator && node.arguments.length == 2) { |
| - Primitive leftArg = getDartReceiver(node); |
| - Primitive rightArg = getDartArgument(node, 0); |
| + Primitive leftArg = node.dartReceiver; |
| + Primitive rightArg = node.getDartArgument(0); |
| AbstractValue left = getValue(leftArg); |
| AbstractValue right = getValue(rightArg); |
| @@ -1003,7 +1032,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // Please see documentation for IsFalsy, StrictEq, and LooseEq. |
| if (left.isNullConstant || right.isNullConstant) { |
| return replaceWithBinary(BuiltinOperator.Identical, |
| - leftArg, rightArg); |
| + leftArg, rightArg, reprocess: true); |
| } |
| // There are several implementations of == that behave like identical. |
| // Specialize it if we definitely call one of those. |
| @@ -1018,8 +1047,10 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| } |
| if (behavesLikeIdentical) { |
| + // Note: Reprocess the returned identical call to try and replace it |
| + // with an equality operator. |
| return replaceWithBinary(BuiltinOperator.Identical, |
| - leftArg, rightArg); |
| + leftArg, rightArg, reprocess: true); |
| } |
| } else { |
| if (lattice.isDefinitelyNum(left, allowNull: trustPrimitives) && |
| @@ -1073,7 +1104,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| } |
| if (node.selector.isOperator && node.arguments.length == 1) { |
| - Primitive argument = getDartReceiver(node); |
| + Primitive argument = node.dartReceiver; |
| AbstractValue value = getValue(argument); |
| if (lattice.isDefinitelyNum(value, allowNull: false)) { |
| @@ -1088,11 +1119,11 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| if (node.selector.isCall) { |
| String name = node.selector.name; |
| - Primitive receiver = getDartReceiver(node); |
| + Primitive receiver = node.dartReceiver; |
| AbstractValue receiverValue = getValue(receiver); |
| if (name == 'remainder') { |
| if (node.arguments.length == 2) { |
| - Primitive arg = getDartArgument(node, 0); |
| + Primitive arg = node.getDartArgument(0); |
| AbstractValue argValue = getValue(arg); |
| if (lattice.isDefinitelyInt(receiverValue) && |
| lattice.isDefinitelyInt(argValue) && |
| @@ -1103,9 +1134,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| } |
| } |
| - // We should only get here if the node was not specialized. |
| - assert(node.parent != null); |
| - return false; |
| + return null; |
| } |
| /// Returns `true` if [value] represents an int value that cannot be zero. |
| @@ -1118,56 +1147,29 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| return backend.isInterceptedSelector(selector); |
| } |
| - Primitive getDartReceiver(InvokeMethod node) { |
| - if (node.receiverIsIntercepted) { |
| - return node.arguments[0].definition; |
| - } else { |
| - return node.receiver.definition; |
| - } |
| - } |
| - |
| - Primitive getDartArgument(InvokeMethod node, int n) { |
| - if (isInterceptedSelector(node.selector)) { |
| - return node.arguments[n+1].definition; |
| - } else { |
| - return node.arguments[n].definition; |
| - } |
| - } |
| - |
| /// If [node] is a getter or setter invocation, tries to replace the |
| /// invocation with a direct access to a field. |
| /// |
| /// Returns `true` if the node was replaced. |
| - bool specializeFieldAccess(InvokeMethod node) { |
| - if (!node.selector.isGetter && !node.selector.isSetter) return false; |
| - AbstractValue receiver = getValue(getDartReceiver(node)); |
| + Primitive specializeFieldAccess(InvokeMethod node) { |
| + if (!node.selector.isGetter && !node.selector.isSetter) return null; |
| + AbstractValue receiver = getValue(node.dartReceiver); |
| Element target = |
| typeSystem.locateSingleElement(receiver.type, node.selector); |
| - if (target is! FieldElement) return false; |
| + if (target is! FieldElement) return null; |
| // TODO(asgerf): Inlining native fields will make some tests pass for the |
| // wrong reason, so for testing reasons avoid inlining them. |
| if (backend.isNative(target) || backend.isJsInterop(target)) { |
| - return false; |
| + return null; |
| } |
| - Continuation cont = node.continuation.definition; |
| if (node.selector.isGetter) { |
| - GetField get = new GetField(getDartReceiver(node), target); |
| - LetPrim let = makeLetPrimInvoke(get, cont); |
| - replaceSubtree(node, let); |
| - push(let); |
| - return true; |
| + return new GetField(node.dartReceiver, target); |
| } else { |
| - if (target.isFinal) return false; |
| - assert(cont.parameters.single.hasNoUses); |
| - cont.parameters.clear(); |
| - CpsFragment cps = new CpsFragment(node.sourceInformation); |
| - cps.letPrim(new SetField(getDartReceiver(node), |
| - target, |
| - getDartArgument(node, 0))); |
| - cps.invokeContinuation(cont); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + if (target.isFinal) return null; |
| + assert(node.hasNoUses); |
| + return new SetField(node.dartReceiver, |
| + target, |
| + node.getDartArgument(0)); |
| } |
| } |
| @@ -1220,65 +1222,55 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /// Tries to replace [node] with a direct `length` or index access. |
| /// |
| /// Returns `true` if the node was replaced. |
| - bool specializeIndexableAccess(InvokeMethod node) { |
| - Primitive receiver = getDartReceiver(node); |
| + specializeIndexableAccess(InvokeMethod node) { |
| + Primitive receiver = node.dartReceiver; |
| AbstractValue receiverValue = getValue(receiver); |
| if (!typeSystem.isDefinitelyIndexable(receiverValue.type, |
| allowNull: true)) { |
| - return false; |
| + return null; |
| } |
| SourceInformation sourceInfo = node.sourceInformation; |
| - Continuation cont = node.continuation.definition; |
| switch (node.selector.name) { |
| case 'length': |
| - if (!node.selector.isGetter) return false; |
| - CpsFragment cps = new CpsFragment(sourceInfo); |
| - cps.invokeContinuation(cont, [cps.letPrim(new GetLength(receiver))]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + if (!node.selector.isGetter) return null; |
| + return new GetLength(receiver); |
| case '[]': |
| - Primitive index = getDartArgument(node, 0); |
| - if (!lattice.isDefinitelyInt(getValue(index))) return false; |
| + Primitive index = node.getDartArgument(0); |
| + if (!lattice.isDefinitelyInt(getValue(index))) return null; |
| CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo); |
| GetIndex get = cps.letPrim(new GetIndex(receiver, index)); |
| - cps.invokeContinuation(cont, [get]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + get.substituteFor(node); |
| + get.hint = node.hint; // TODO(asgerf): Make substituteFor set the hint? |
| + return cps; |
| case '[]=': |
| if (!typeSystem.isDefinitelyMutableIndexable(receiverValue.type, |
| allowNull: true)) { |
| - return false; |
| + return null; |
| } |
| - Primitive index = getDartArgument(node, 0); |
| - Primitive value = getDartArgument(node, 1); |
| - if (!lattice.isDefinitelyInt(getValue(index))) return false; |
| + Primitive index = node.getDartArgument(0); |
| + Primitive value = node.getDartArgument(1); |
| + if (!lattice.isDefinitelyInt(getValue(index))) return null; |
| CpsFragment cps = makeBoundsCheck(receiver, index, sourceInfo); |
| cps.letPrim(new SetIndex(receiver, index, value)); |
| - assert(cont.parameters.single.hasNoUses); |
| - cont.parameters.clear(); |
| - cps.invokeContinuation(cont, []); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + assert(node.hasNoUses); |
| + return cps; |
| default: |
| - return false; |
| + return null; |
| } |
| } |
| /// Tries to replace [node] with one or more direct array access operations. |
| /// |
| /// Returns `true` if the node was replaced. |
| - bool specializeArrayAccess(InvokeMethod node) { |
| - Primitive list = getDartReceiver(node); |
| + CpsFragment specializeArrayAccess(InvokeMethod node) { |
| + Primitive list = node.dartReceiver; |
| AbstractValue listValue = getValue(list); |
| // Ensure that the object is a native list or null. |
| if (!lattice.isDefinitelyArray(listValue, allowNull: true)) { |
| - return false; |
| + return null; |
| } |
| bool isFixedLength = |
| lattice.isDefinitelyFixedArray(listValue, allowNull: true); |
| @@ -1287,31 +1279,29 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| bool isExtendable = |
| lattice.isDefinitelyExtendableArray(listValue, allowNull: true); |
| SourceInformation sourceInfo = node.sourceInformation; |
| - Continuation cont = node.continuation.definition; |
| switch (node.selector.name) { |
| case 'add': |
| if (!node.selector.isCall || |
| node.selector.positionalArgumentCount != 1 || |
| node.selector.namedArgumentCount != 0) { |
| - return false; |
| + return null; |
| } |
| - if (!isExtendable) return false; |
| - Primitive addedItem = getDartArgument(node, 0); |
| + if (!isExtendable) return null; |
| + Primitive addedItem = node.getDartArgument(0); |
| CpsFragment cps = new CpsFragment(sourceInfo); |
| cps.invokeBuiltin(BuiltinMethod.Push, |
| list, |
| <Primitive>[addedItem]); |
| - cps.invokeContinuation(cont, [cps.makeNull()]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + // TODO(asgerf): Only make null if node has uses. |
| + cps.makeNull().substituteFor(node); |
| + return cps; |
| case 'removeLast': |
| if (!node.selector.isCall || |
| node.selector.argumentCount != 0) { |
| - return false; |
| + return null; |
| } |
| - if (!isExtendable) return false; |
| + if (!isExtendable) return null; |
| CpsFragment cps = new CpsFragment(sourceInfo); |
| Primitive length = cps.letPrim(new GetLength(list)); |
| Primitive isEmpty = cps.applyBuiltin( |
| @@ -1324,86 +1314,82 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| Primitive removedItem = cps.invokeBuiltin(BuiltinMethod.Pop, |
| list, |
| <Primitive>[]); |
| - cps.invokeContinuation(cont, [removedItem]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + removedItem.hint = node.hint; |
| + removedItem.substituteFor(node); |
| + return cps; |
| case 'addAll': |
| if (!node.selector.isCall || |
| node.selector.argumentCount != 1) { |
| - return false; |
| + return null; |
| } |
| - if (!isExtendable) return false; |
| - Primitive addedList = getDartArgument(node, 0); |
| + if (!isExtendable) return null; |
| + Primitive addedList = node.getDartArgument(0); |
| // Rewrite addAll([x1, ..., xN]) to push(x1, ..., xN). |
| // Ensure that the list is not mutated between creation and use. |
| // We aim for the common case where this is the only use of the list, |
| // which also guarantees that this list is not mutated before use. |
| if (addedList is! LiteralList || !addedList.hasExactlyOneUse) { |
| - return false; |
| + return null; |
| } |
| LiteralList addedLiteral = addedList; |
| CpsFragment cps = new CpsFragment(sourceInfo); |
| cps.invokeBuiltin(BuiltinMethod.Push, |
| list, |
| addedLiteral.values.map((ref) => ref.definition).toList()); |
| - cps.invokeContinuation(cont, [cps.makeNull()]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + cps.makeNull().substituteFor(node); |
| + return cps; |
| case 'elementAt': |
| if (!node.selector.isCall || |
| node.selector.positionalArgumentCount != 1 || |
| node.selector.namedArgumentCount != 0) { |
| - return false; |
| + return null; |
| } |
| - if (listValue.isNullable) return false; |
| - Primitive index = getDartArgument(node, 0); |
| - if (!lattice.isDefinitelyInt(getValue(index))) return false; |
| + if (listValue.isNullable) return null; |
| + Primitive index = node.getDartArgument(0); |
| + if (!lattice.isDefinitelyInt(getValue(index))) return null; |
| CpsFragment cps = makeBoundsCheck(list, index, sourceInfo); |
| GetIndex get = cps.letPrim(new GetIndex(list, index)); |
| - cps.invokeContinuation(cont, [get]); |
| - replaceSubtree(node, cps.result); |
| - push(cps.result); |
| - return true; |
| + get.hint = node.hint; |
| + get.substituteFor(node); |
| + return cps; |
| case 'forEach': |
| Element element = |
| compiler.world.locateSingleElement(node.selector, listValue.type); |
| if (element == null || |
| !element.isFunction || |
| - !node.selector.isCall) return false; |
| + !node.selector.isCall) return null; |
| assert(node.selector.positionalArgumentCount == 1); |
| assert(node.selector.namedArgumentCount == 0); |
| FunctionDefinition target = functionCompiler.compileToCpsIr(element); |
| - node.receiver.definition.substituteFor(target.thisParameter); |
| - for (int i = 0; i < node.arguments.length; ++i) { |
| - node.arguments[i].definition.substituteFor(target.parameters[i]); |
| - } |
| - node.continuation.definition.substituteFor(target.returnContinuation); |
| - |
| - replaceSubtree(node, target.body); |
| - push(target.body); |
| - return true; |
| + CpsFragment cps = new CpsFragment(node.sourceInformation); |
| + Primitive result = cps.inlineFunction(target, |
| + node.arguments.map((ref) => ref.definition).toList(), |
| + node.receiver.definition, |
| + hint: node.hint); |
| + result.substituteFor(node); |
| + return cps; |
| case 'iterator': |
| - if (!node.selector.isGetter) return false; |
| - Primitive iterator = cont.parameters.single; |
| - Continuation iteratorCont = cont; |
| + // TODO(asgerf): This should be done differently. |
| + // The types recomputed in a very error-prone manner. |
| + if (!node.selector.isGetter) return null; |
| + Primitive iterator = node; |
| + LetPrim iteratorBinding = node.parent; |
| // Check that all uses of the iterator are 'moveNext' and 'current'. |
| assert(!isInterceptedSelector(Selectors.moveNext)); |
| assert(!isInterceptedSelector(Selectors.current)); |
| for (Reference ref in iterator.effectiveUses) { |
| - if (ref.parent is! InvokeMethod) return false; |
| + if (ref.parent is! InvokeMethod) return null; |
| InvokeMethod use = ref.parent; |
| - if (ref != use.receiver) return false; |
| + if (ref != use.receiver) return null; |
| if (use.selector != Selectors.moveNext && |
| use.selector != Selectors.current) { |
| - return false; |
| + return null; |
| } |
| } |
| @@ -1416,17 +1402,14 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // Rewrite all uses of the iterator. |
| for (Reference ref in iterator.effectiveUses) { |
| InvokeMethod use = ref.parent; |
| - Continuation useCont = use.continuation.definition; |
| if (use.selector == Selectors.current) { |
| // Rewrite iterator.current to a use of the 'current' variable. |
| - Parameter result = useCont.parameters.single; |
| - if (result.hint != null) { |
| + if (use.hint != null) { |
| // If 'current' was originally moved into a named variable, use |
| // that variable name for the mutable variable. |
| - current.hint = result.hint; |
| + current.hint = use.hint; |
| } |
| - LetPrim let = makeLetPrimInvoke(new GetMutable(current), useCont); |
| - replaceSubtree(use, let); |
| + use.redefineAs(new GetMutable(current)); |
| } else { |
| assert (use.selector == Selectors.moveNext); |
| // Rewrite iterator.moveNext() to: |
| @@ -1444,6 +1427,8 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // [cps] contains the code we insert instead of moveNext(). |
| CpsFragment cps = new CpsFragment(node.sourceInformation); |
| + Parameter result = new Parameter(node.hint); |
| + Continuation moveNextCont = cps.letCont(<Parameter>[result]); |
| // We must check for concurrent modification when calling moveNext. |
| // When moveNext is used as a loop condition, the check prevents |
| @@ -1471,7 +1456,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // |
| // The check before the loop can often be eliminated because it |
| // follows immediately after the 'iterator' call. |
| - InteriorNode parent = getEffectiveParent(use); |
| + InteriorNode parent = getEffectiveParent(use.parent); |
| if (!isFixedLength) { |
| if (parent is Continuation && parent.isRecursive) { |
| // Check for concurrent modification before every invocation |
| @@ -1482,7 +1467,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| ref != null; |
| ref = ref.next) { |
| Expression invocationCaller = ref.parent; |
| - if (getEffectiveParent(invocationCaller) == iteratorCont) { |
| + if (getEffectiveParent(invocationCaller) == iteratorBinding) { |
| // No need to check for concurrent modification immediately |
| // after the call to 'iterator'. |
| continue; |
| @@ -1506,18 +1491,26 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| CpsFragment falseBranch = cps.ifFalsy(hasMore); |
| falseBranch |
| ..setMutable(current, falseBranch.makeNull()) |
| - ..invokeContinuation(useCont, [falseBranch.makeFalse()]); |
| + ..invokeContinuation(moveNextCont, [falseBranch.makeFalse()]); |
| // Return true if there are more element. |
| + current.type = typeSystem.elementTypeOfIndexable(listValue.type); |
| cps.setMutable(current, |
| cps.letPrim(new GetIndex(list, cps.getMutable(index)))); |
| cps.setMutable(index, cps.applyBuiltin( |
| BuiltinOperator.NumAdd, |
| [cps.getMutable(index), cps.makeOne()])); |
| - cps.invokeContinuation(useCont, [cps.makeTrue()]); |
| + cps.invokeContinuation(moveNextCont, [cps.makeTrue()]); |
| + |
| + reanalyzeFragment(cps); |
| // Replace the moveNext() call. It will be visited later. |
| - replaceSubtree(use, cps.result); |
| + LetPrim let = use.parent; |
| + cps.context = moveNextCont; |
| + cps.insertBelow(let); |
| + let.remove(); |
| + result.substituteFor(use); |
| + use.destroy(); |
| } |
| } |
| @@ -1529,36 +1522,11 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| cps.letMutable(index, cps.makeZero()); |
| cps.letMutable(current, cps.makeNull()); |
| cps.letPrim(originalLength); |
| - |
| - // Insert this fragment before the continuation body and replace the |
| - // iterator call with a call to the continuation without arguments. |
| - // For scoping reasons, the variables must be bound inside the |
| - // continuation, not at the invocation-site. |
| - iteratorCont.parameters.clear(); |
| - insertBefore(iteratorCont.body, cps); |
| - InvokeContinuation invoke = new InvokeContinuation(iteratorCont, []); |
| - replaceSubtree(node, invoke); |
| - push(invoke); |
| - return true; |
| + return cps; |
| default: |
| - return false; |
| - } |
| - } |
| - |
| - /// If [prim] is the parameter to a call continuation, returns the |
| - /// corresponding call. |
| - CallExpression getCallWithResult(Primitive prim) { |
| - if (prim is Parameter && prim.parent is Continuation) { |
| - Continuation cont = prim.parent; |
| - if (cont.hasExactlyOneUse) { |
| - Node use = cont.firstRef.parent; |
| - if (use is CallExpression) { |
| - return use; |
| - } |
| - } |
| + return null; |
| } |
| - return null; |
| } |
| /// Returns the first parent of [node] that is not a pure expression. |
| @@ -1582,14 +1550,14 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /// => |
| /// obj.foo$<n>(<args>) |
| /// |
| - bool specializeClosureCall(InvokeMethod node) { |
| + Primitive specializeClosureCall(InvokeMethod node) { |
| Selector call = node.selector; |
| - if (!call.isClosureCall) return false; |
| + if (!call.isClosureCall) return null; |
| assert(!isInterceptedSelector(call)); |
| assert(call.argumentCount == node.arguments.length); |
| - Primitive tearOff = node.receiver.definition.effectiveDefinition; |
| + Primitive tearOff = node.dartReceiver.effectiveDefinition; |
| // Note: We don't know if [tearOff] is actually a tear-off. |
| // We name variables based on the pattern we are trying to match. |
| @@ -1598,38 +1566,33 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| FunctionSignature signature = target.functionSignature; |
| // If the selector does not apply, don't bother (will throw at runtime). |
| - if (!call.signatureApplies(target)) return false; |
| + if (!call.signatureApplies(target)) return null; |
| // If some optional arguments are missing, give up. |
| // TODO(asgerf): Improve optimization by inserting default arguments. |
| - if (call.argumentCount != signature.parameterCount) return false; |
| + if (call.argumentCount != signature.parameterCount) return null; |
| - InvokeStatic invoke = new InvokeStatic.byReference( |
| - target, |
| + // Replace with InvokeStatic. |
| + // The tear-off will be cleaned up by shrinking reductions. |
| + return new InvokeStatic(target, |
| new Selector.fromElement(target), |
| - node.arguments, |
| - node.continuation, |
| + node.arguments.map((ref) => ref.definition).toList(), |
| node.sourceInformation); |
| - node.receiver.unlink(); |
| - replaceSubtree(node, invoke, unlink: false); |
| - push(invoke); |
| - return true; |
| } |
| - CallExpression tearOffInvoke = getCallWithResult(tearOff); |
| - if (tearOffInvoke is InvokeMethod && tearOffInvoke.selector.isGetter) { |
| - Selector getter = tearOffInvoke.selector; |
| + if (tearOff is InvokeMethod && tearOff.selector.isGetter) { |
| + Selector getter = tearOff.selector; |
| // TODO(asgerf): Support torn-off intercepted methods. |
| - if (isInterceptedSelector(getter)) return false; |
| + if (isInterceptedSelector(getter)) return null; |
| - Continuation getterCont = tearOffInvoke.continuation.definition; |
| + LetPrim tearOffBinding = tearOff.parent; |
| - Primitive object = tearOffInvoke.receiver.definition; |
| + Primitive object = tearOff.receiver.definition; |
| // Ensure that the object actually has a foo member, since we might |
| // otherwise alter a noSuchMethod call. |
| TypeMask type = getValue(object).type; |
| - if (typeSystem.needsNoSuchMethodHandling(type, getter)) return false; |
| + if (typeSystem.needsNoSuchMethodHandling(type, getter)) return null; |
| // Determine if the getter invocation can have side-effects. |
| Element element = typeSystem.locateSingleElement(type, getter); |
| @@ -1637,30 +1600,28 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // If there are multiple uses, we cannot eliminate the getter call and |
| // therefore risk duplicating its side effects. |
| - if (!isPure && tearOff.hasMultipleEffectiveUses) return false; |
| + if (!isPure && tearOff.hasMultipleEffectiveUses) return null; |
| // If the getter call is impure, we risk reordering side effects. |
|
sra1
2015/11/19 21:41:46
... unless the tear-off is immediatley prior to th
asgerf
2015/11/20 16:23:54
Done.
|
| - if (!isPure && getEffectiveParent(node) != getterCont) { |
| - return false; |
| + if (!isPure && getEffectiveParent(node.parent) != tearOffBinding) { |
| + return null; |
| } |
| - InvokeMethod invoke = new InvokeMethod.byReference( |
| - new Reference<Primitive>(object), |
| + InvokeMethod invoke = new InvokeMethod( |
| + object, |
| new Selector.call(getter.memberName, call.callStructure), |
| type, |
| - node.arguments, |
| - node.continuation, |
| + node.arguments.map((ref) => ref.definition).toList(), |
| node.sourceInformation); |
| - node.receiver.unlink(); |
| - replaceSubtree(node, invoke, unlink: false); |
| + node.receiver.changeTo(new Parameter(null)); // Remove the tear off use. |
| if (tearOff.hasNoEffectiveUses) { |
| // Eliminate the getter call if it has no more uses. |
| // This cannot be delegated to other optimizations because we need to |
| // avoid duplication of side effects. |
| destroyRefinementsOfDeadPrimitive(tearOff); |
| - getterCont.parameters.clear(); |
| - replaceSubtree(tearOffInvoke, new InvokeContinuation(getterCont, [])); |
| + tearOff.destroy(); |
| + tearOffBinding.remove(); |
| } else { |
| // There are more uses, so we cannot eliminate the getter call. This |
| // means we duplicated the effects of the getter call, but we should |
| @@ -1668,10 +1629,9 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| assert(isPure); |
| } |
| - push(invoke); |
| - return true; |
| + return invoke; |
| } |
| - return false; |
| + return null; |
| } |
| void destroyRefinementsOfDeadPrimitive(Primitive prim) { |
| @@ -1688,39 +1648,39 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /// Inlines a single-use closure if it leaves the closure object with only |
| /// field accesses. This is optimized later by [ScalarReplacer]. |
| - bool specializeSingleUseClosureCall(InvokeMethod node) { |
| + CpsFragment specializeSingleUseClosureCall(InvokeMethod node) { |
| Selector call = node.selector; |
| - if (!call.isClosureCall) return false; |
| + if (!call.isClosureCall) return null; |
| assert(!isInterceptedSelector(call)); |
| assert(call.argumentCount == node.arguments.length); |
| Primitive receiver = node.receiver.definition; |
| - if (receiver is !CreateInstance) return false; |
| + if (receiver is !CreateInstance) return null; |
| CreateInstance createInstance = receiver; |
| - if (!createInstance.hasExactlyOneUse) return false; |
| + if (!createInstance.hasExactlyOneUse) return null; |
| // Inline only closures. This avoids inlining the 'call' method of a class |
| // that has many allocation sites. |
| - if (createInstance.classElement is !ClosureClassElement) return false; |
| + if (createInstance.classElement is !ClosureClassElement) return null; |
| ClosureClassElement closureClassElement = createInstance.classElement; |
| Element element = closureClassElement.localLookup(Identifiers.call); |
| - if (element == null || !element.isFunction) return false; |
| + if (element == null || !element.isFunction) return null; |
| FunctionElement functionElement = element; |
| - if (functionElement.asyncMarker != AsyncMarker.SYNC) return false; |
| + if (functionElement.asyncMarker != AsyncMarker.SYNC) return null; |
| - if (!call.signatureApplies(functionElement)) return false; |
| + if (!call.signatureApplies(functionElement)) return null; |
| // Inline only for exact match. |
| // TODO(sra): Handle call with defaulted arguments. |
| Selector targetSelector = new Selector.fromElement(functionElement); |
| - if (call.callStructure != targetSelector.callStructure) return false; |
| + if (call.callStructure != targetSelector.callStructure) return null; |
| // Don't inline if [target] contains try-catch or try-finally. JavaScript |
| // engines typically do poor optimization of the entire function containing |
| // the 'try'. |
| - if (functionElement.resolvedAst.elements.containsTryStatement) return false; |
| + if (functionElement.resolvedAst.elements.containsTryStatement) return null; |
| FunctionDefinition target = |
| functionCompiler.compileToCpsIr(functionElement); |
| @@ -1736,53 +1696,34 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // Closures do not currently have writable fields, but closure conversion |
| // could esily be changed to allocate some cells in a closure object. |
| if (use is SetField && ref == use.object) continue; |
| - return false; |
| - } |
| - |
| - node.receiver.definition.substituteFor(target.thisParameter); |
| - for (int i = 0; i < node.arguments.length; ++i) { |
| - node.arguments[i].definition.substituteFor(target.parameters[i]); |
| + return null; |
| } |
| - node.continuation.definition.substituteFor(target.returnContinuation); |
| - replaceSubtree(node, target.body); |
| - push(target.body); |
| - return true; |
| - } |
| - |
| - /// Side-effect free expressions with constant results are be replaced by: |
| - /// |
| - /// (LetPrim p = constant (InvokeContinuation k p)). |
| - /// |
| - /// The new expression will be visited. |
| - /// |
| - /// Returns true if the node was replaced. |
| - bool constifyExpression(CallExpression node) { |
| - Continuation continuation = node.continuation.definition; |
| - ConstantValue constant = replacements[node]; |
| - if (constant == null) return false; |
| - Constant primitive = makeConstantPrimitive(constant); |
| - LetPrim letPrim = makeLetPrimInvoke(primitive, continuation); |
| - replaceSubtree(node, letPrim); |
| - push(letPrim); |
| - return true; |
| + CpsFragment cps = new CpsFragment(node.sourceInformation); |
| + Primitive returnValue = cps.inlineFunction(target, |
| + node.arguments.map((ref) => ref.definition).toList(), |
| + node.receiver.definition, |
| + hint: node.hint); |
| + returnValue.substituteFor(node); |
| + return cps; |
| } |
| - void visitInvokeMethod(InvokeMethod node) { |
| - if (constifyExpression(node)) return; |
| - if (specializeOperatorCall(node)) return; |
| - if (specializeFieldAccess(node)) return; |
| - if (specializeIndexableAccess(node)) return; |
| - if (specializeArrayAccess(node)) return; |
| - if (specializeSingleUseClosureCall(node)) return; |
| - if (specializeClosureCall(node)) return; |
| + visitInvokeMethod(InvokeMethod node) { |
| + var specialized = |
| + specializeOperatorCall(node) ?? |
| + specializeFieldAccess(node) ?? |
| + specializeIndexableAccess(node) ?? |
| + specializeArrayAccess(node) ?? |
| + specializeSingleUseClosureCall(node) ?? |
| + specializeClosureCall(node); |
| + if (specialized != null) return specialized; |
| node.mask = |
| - typeSystem.intersection(node.mask, getValue(getDartReceiver(node)).type); |
| + typeSystem.intersection(node.mask, getValue(node.dartReceiver).type); |
| AbstractValue receiver = getValue(node.receiver.definition); |
| - if (node.receiverIsIntercepted && |
| + if (node.callingConvention == CallingConvention.Intercepted && |
| node.receiver.definition.sameValue(node.arguments[0].definition)) { |
| // The receiver and first argument are the same; that means we already |
| // determined in visitInterceptor that we are targeting a non-interceptor. |
| @@ -1802,81 +1743,51 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // Replace the extra receiver argument with a dummy value if the |
| // target definitely does not use it. |
| Constant dummy = makeConstantPrimitive(new IntConstantValue(0)); |
| - insertLetPrim(node, dummy); |
| + new LetPrim(dummy).insertAbove(node.parent); |
| node.arguments[0].changeTo(dummy); |
| - node.receiverIsIntercepted = false; |
| + node.callingConvention = CallingConvention.DummyIntercepted; |
| } |
| } |
| } |
| - void visitTypeCast(TypeCast node) { |
| - Continuation cont = node.continuation.definition; |
| - |
| + Primitive visitTypeCast(TypeCast node) { |
| AbstractValue value = getValue(node.value.definition); |
| switch (lattice.isSubtypeOf(value, node.dartType, allowNull: true)) { |
| case AbstractBool.Maybe: |
| case AbstractBool.Nothing: |
| - break; |
| + return null; |
| case AbstractBool.True: |
| - // Cast always succeeds, replace it with InvokeContinuation. |
| - InvokeContinuation invoke = |
| - new InvokeContinuation(cont, <Primitive>[node.value.definition]); |
| - replaceSubtree(node, invoke); |
| - push(invoke); |
| - return; |
| + // Return an unused primitive that will be removed again. |
| + node.value.definition.substituteFor(node); |
| + return makeConstantPrimitive(new NullConstantValue()); |
| case AbstractBool.False: |
| // Cast always fails, remove unreachable continuation body. |
| - replaceSubtree(cont.body, new Unreachable()); |
| - break; |
| + LetPrim letPrim = node.parent; |
| + replaceSubtree(letPrim.body, new Unreachable()); |
| + return null; |
| } |
| } |
| /// Specialize calls to internal static methods. |
| - /// |
| - /// Returns true if the call was replaced. |
| - bool specializeInternalMethodCall(InvokeStatic node) { |
| - // TODO(asgerf): This is written to easily scale to more cases, |
| - // either add more cases or clean up. |
| - Continuation cont = node.continuation.definition; |
| - Primitive arg(int n) => node.arguments[n].definition; |
| - AbstractValue argType(int n) => getValue(arg(n)); |
| - |
| - bool replaceWithBinary(BuiltinOperator operator, |
| - Primitive left, |
| - Primitive right) { |
| - Primitive prim = |
| - new ApplyBuiltinOperator(operator, <Primitive>[left, right], |
| - node.sourceInformation); |
| - LetPrim let = makeLetPrimInvoke(prim, cont); |
| - replaceSubtree(node, let); |
| - push(let); |
| - return true; // So returning early is more convenient. |
| - } |
| - |
| - if (node.target.library.isInternalLibrary) { |
| - switch(node.target.name) { |
| - case InternalMethod.Stringify: |
| - if (lattice.isDefinitelyString(argType(0))) { |
| - InvokeContinuation invoke = |
| - new InvokeContinuation(cont, <Primitive>[arg(0)]); |
| - replaceSubtree(node, invoke); |
| - push(invoke); |
| - return true; |
| - } |
| - break; |
| + specializeInternalMethodCall(InvokeStatic node) { |
| + if (node.target == backend.helpers.stringInterpolationHelper) { |
| + AbstractValue value = getValue(node.arguments[0].definition); |
| + if (lattice.isDefinitelyString(value)) { |
| + node.arguments[0].definition.substituteFor(node); |
| + return new CpsFragment(); |
| } |
| - } else if (node.target.library.isDartCore) { |
| - switch(node.target.name) { |
| - case CorelibMethod.Identical: |
| - if (node.arguments.length == 2) { |
| - return replaceWithBinary(BuiltinOperator.Identical, arg(0), arg(1)); |
| - } |
| - break; |
| + } else if (node.target == compiler.identicalFunction) { |
| + if (node.arguments.length == 2) { |
| + Primitive newPrim = new ApplyBuiltinOperator(BuiltinOperator.Identical, |
| + [node.arguments[0].definition, node.arguments[1].definition], |
| + node.sourceInformation); |
| + newPrim.substituteFor(node); |
| + return new CpsFragment()..letPrim(newPrim); |
| } |
| } |
| - return false; |
| + return null; |
| } |
| /// Try to inline static invocations. |
| @@ -1886,13 +1797,13 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /// |
| /// * Inline functions with a single expression statement or return statement |
| /// provided that the subexpression is an invocation of foreign code. |
| - bool inlineInvokeStatic(InvokeStatic node) { |
| + inlineInvokeStatic(InvokeStatic node) { |
| // The target might not have an AST, for example if it deferred. |
| - if (!node.target.hasNode) return false; |
| + if (!node.target.hasNode) return null; |
| if (node.target.asyncMarker != AsyncMarker.SYNC) { |
| // Inlining of async/sync*/async* methods is currently not supported. |
| - return false; |
| + return null; |
| } |
| // True if an expression is non-expansive, in the sense defined by this |
| @@ -1952,23 +1863,21 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| return false; |
| } |
| - if (!shouldInline()) return false; |
| + if (!shouldInline()) return null; |
| FunctionDefinition target = functionCompiler.compileToCpsIr(node.target); |
| - for (int i = 0; i < node.arguments.length; ++i) { |
| - node.arguments[i].definition.substituteFor(target.parameters[i]); |
| - } |
| - node.continuation.definition.substituteFor(target.returnContinuation); |
| - replaceSubtree(node, target.body); |
| - push(target.body); |
| - return true; |
| + CpsFragment cps = new CpsFragment(node.sourceInformation); |
| + Primitive result = cps.inlineFunction(target, |
| + node.arguments.map((ref) => ref.definition).toList(), |
| + null, |
| + hint: node.hint); |
| + result.substituteFor(node); |
| + return cps; |
| } |
| - void visitInvokeStatic(InvokeStatic node) { |
| - if (constifyExpression(node)) return; |
| - if (specializeInternalMethodCall(node)) return; |
| - if (inlineInvokeStatic(node)) return; |
| + visitInvokeStatic(InvokeStatic node) { |
| + return specializeInternalMethodCall(node) ?? inlineInvokeStatic(node); |
| } |
| AbstractValue getValue(Variable node) { |
| @@ -1985,9 +1894,15 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| /*************************** PRIMITIVES **************************/ |
| // |
| - // The visit method for a primitive may optionally return a new |
| - // primitive. If non-null, the surrounding LetPrim will substitute it |
| - // and bind the new primitive instead. |
| + // The visit method for a primitive may return one of the following: |
| + // - Primitive: |
| + // The visited primitive will be replaced by the returned primitive. |
| + // The type of the primitive will be recomputed. |
| + // - CpsFragment: |
| + // The primitive binding will be destroyed and replaced by the given |
| + // code fragment. All types in the fragment will be recomputed. |
| + // - Null: |
| + // Nothing happens. The primitive remains as it is. |
| // |
| void visitApplyBuiltinOperator(ApplyBuiltinOperator node) { |
| @@ -2021,7 +1936,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| Constant prim = |
| makeConstantPrimitive(new StringConstantValue(string)); |
| - insertLetPrim(node.parent, prim); |
| + new LetPrim(prim).insertAbove(node.parent); |
| for (int k = startOfSequence; k < i; ++k) { |
| node.arguments[k].unlink(); |
| node.arguments[k] = null; // Remove the argument after the loop. |
| @@ -2047,7 +1962,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| right.constant.isTrue) { |
| // Replace identical(x, true) by x when x is known to be a boolean. |
| // Note that this is not safe if x is null, because the value might |
| - // not be used as a condition. A rule for [IsTrue] handles that case. |
| + // not be used as a condition. |
| leftArg.substituteFor(node); |
| } else if (lattice.isDefinitelyBool(right) && |
| left.isConstant && |
| @@ -2081,29 +1996,6 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| } |
| void visitApplyBuiltinMethod(ApplyBuiltinMethod node) { |
| - if (node.method == BuiltinMethod.Push) { |
| - // Convert consecutive pushes into a single push. |
| - InteriorNode parent = getEffectiveParent(node.parent); |
| - if (parent is LetPrim && parent.primitive is ApplyBuiltinMethod) { |
| - ApplyBuiltinMethod previous = parent.primitive; |
| - if (previous.method == BuiltinMethod.Push && |
| - previous.receiver.definition.sameValue(node.receiver.definition)) { |
| - // We found two consecutive pushes. |
| - // Move all arguments from the first push onto the second one. |
| - List<Reference<Primitive>> arguments = previous.arguments; |
| - for (Reference ref in arguments) { |
| - ref.parent = node; |
| - } |
| - arguments.addAll(node.arguments); |
| - node.arguments = arguments; |
| - // Elimnate the old push. |
| - previous.receiver.unlink(); |
| - assert(previous.hasNoUses); |
| - parent.parent.body = parent.body; |
| - parent.body.parent = parent.parent; |
| - } |
| - } |
| - } |
| } |
| Primitive visitTypeTest(TypeTest node) { |
| @@ -2168,7 +2060,7 @@ class TransformingVisitor extends DeepRecursiveVisitor { |
| // This has lower priority than the 'typeof'-based tests because |
| // 'typeof' expressions might give the VM some more useful information. |
| Primitive nullConst = makeConstantPrimitive(new NullConstantValue()); |
| - insertLetPrim(node.parent, nullConst); |
| + new LetPrim(nullConst).insertAbove(node.parent); |
| return new ApplyBuiltinOperator( |
| BuiltinOperator.LooseNeq, |
| <Primitive>[prim, nullConst], |
| @@ -2410,13 +2302,8 @@ class TypePropagationVisitor implements Visitor { |
| // Access through [getValue] and [setValue]. |
| final Map<Variable, ConstantValue> values; |
| - /// Expressions that invoke their call continuation with a constant value |
| - /// and without any side effects. These can be replaced by the constant. |
| - final Map<Expression, ConstantValue> replacements; |
| - |
| TypePropagationVisitor(this.lattice, |
| this.values, |
| - this.replacements, |
| this.internalError); |
| void analyze(FunctionDefinition root) { |
| @@ -2501,44 +2388,22 @@ class TypePropagationVisitor implements Visitor { |
| defWorklist.add(node); |
| } |
| - /// Updates the value of a [CallExpression]'s continuation parameter. |
| - void setResult(CallExpression call, |
| + /// Sets the type of the given primitive. |
| + /// |
| + /// If [updateValue] is a constant and [canReplace] is true, the primitive |
| + /// is also marked as safe for elimination, so it can be constant-folded. |
| + void setResult(UnsafePrimitive prim, |
| AbstractValue updateValue, |
| {bool canReplace: false}) { |
| - Continuation cont = call.continuation.definition; |
| - setValue(cont.parameters.single, updateValue); |
| - if (!updateValue.isNothing) { |
| - setReachable(cont); |
| - |
| - if (updateValue.isConstant && canReplace) { |
| - replacements[call] = updateValue.constant; |
| - } else { |
| - // A replacement might have been set in a previous iteration. |
| - replacements.remove(call); |
| - } |
| - } |
| + // TODO(asgerf): Separate constant folding from side effect analysis. |
| + setValue(prim, updateValue); |
| + prim.isSafeForElimination = canReplace && updateValue.isConstant; |
|
sra1
2015/11/19 21:41:46
Maybe it was already safe for elimination by some
asgerf
2015/11/20 16:23:54
We have to clear the flag, since 'setResult' may b
|
| } |
| bool isInterceptedSelector(Selector selector) { |
| return backend.isInterceptedSelector(selector); |
| } |
| - Primitive getDartReceiver(InvokeMethod node) { |
| - if (node.receiverIsIntercepted) { |
| - return node.arguments[0].definition; |
| - } else { |
| - return node.receiver.definition; |
| - } |
| - } |
| - |
| - Primitive getDartArgument(InvokeMethod node, int n) { |
| - if (isInterceptedSelector(node.selector)) { |
| - return node.arguments[n+1].definition; |
| - } else { |
| - return node.arguments[n].definition; |
| - } |
| - } |
| - |
| // -------------------------- Visitor overrides ------------------------------ |
| void visit(Node node) { node.accept(this); } |
| @@ -2601,13 +2466,10 @@ class TypePropagationVisitor implements Visitor { |
| } |
| void visitInvokeStatic(InvokeStatic node) { |
| - if (node.target.library != null && node.target.library.isInternalLibrary) { |
| - switch (node.target.name) { |
| - case InternalMethod.Stringify: |
| - AbstractValue argValue = getValue(node.arguments[0].definition); |
| - setResult(node, lattice.stringify(argValue), canReplace: true); |
| - return; |
| - } |
| + if (node.target == backend.helpers.stringInterpolationHelper) { |
| + AbstractValue argValue = getValue(node.arguments[0].definition); |
| + setResult(node, lattice.stringify(argValue), canReplace: true); |
| + return; |
| } |
| TypeMask returnType = typeSystem.getReturnType(node.target); |
| @@ -2631,12 +2493,13 @@ class TypePropagationVisitor implements Visitor { |
| AbstractValue receiver = getValue(node.receiver.definition); |
| node.receiverIsNotNull = receiver.isDefinitelyNotNull; |
| if (receiver.isNothing) { |
| + setResult(node, lattice.nothing); |
| return; // And come back later. |
| } |
| // Constant fold known length of containers. |
| if (node.selector == Selectors.length) { |
| - AbstractValue object = getValue(getDartReceiver(node)); |
| + AbstractValue object = getValue(node.dartReceiver); |
| if (typeSystem.isDefinitelyIndexable(object.type, allowNull: true)) { |
| int length = typeSystem.getContainerLength(object.type.nonNullable()); |
| if (length != null) { |
| @@ -2656,7 +2519,7 @@ class TypePropagationVisitor implements Visitor { |
| AbstractValue result; |
| String opname = node.selector.name; |
| if (node.arguments.length == 1) { |
| - AbstractValue argument = getValue(getDartReceiver(node)); |
| + AbstractValue argument = getValue(node.dartReceiver); |
| // Unary operator. |
| if (opname == "unary-") { |
| opname = "-"; |
| @@ -2665,8 +2528,8 @@ class TypePropagationVisitor implements Visitor { |
| result = lattice.unaryOp(operator, argument); |
| } else if (node.arguments.length == 2) { |
| // Binary operator. |
| - AbstractValue left = getValue(getDartReceiver(node)); |
| - AbstractValue right = getValue(getDartArgument(node, 0)); |
| + AbstractValue left = getValue(node.dartReceiver); |
| + AbstractValue right = getValue(node.getDartArgument(0)); |
| BinaryOperator operator = BinaryOperator.parse(opname); |
| result = lattice.binaryOp(operator, left, right); |
| } |
| @@ -2717,6 +2580,7 @@ class TypePropagationVisitor implements Visitor { |
| for (Reference<Primitive> arg in node.arguments) { |
| AbstractValue value = getValue(arg.definition); |
| if (value.isNothing) { |
| + setValue(node, lattice.nothing); |
| return; // And come back later |
| } else if (value.isConstant && |
| value.constant.isString && |
| @@ -2744,8 +2608,8 @@ class TypePropagationVisitor implements Visitor { |
| ConstantValue leftValue = leftConst.constant; |
| ConstantValue rightValue = rightConst.constant; |
| if (leftConst.isNothing || rightConst.isNothing) { |
| - // Come back later. |
| - return; |
| + setValue(node, lattice.nothing); |
| + return; // And come back later. |
| } else if (!leftConst.isConstant || !rightConst.isConstant) { |
| TypeMask leftType = leftConst.type; |
| TypeMask rightType = rightConst.type; |
| @@ -2943,27 +2807,26 @@ class TypePropagationVisitor implements Visitor { |
| } |
| void visitTypeCast(TypeCast node) { |
| - Continuation cont = node.continuation.definition; |
| AbstractValue input = getValue(node.value.definition); |
| switch (lattice.isSubtypeOf(input, node.dartType, allowNull: true)) { |
| case AbstractBool.Nothing: |
| + setValue(node, lattice.nothing); |
| break; // And come back later. |
| case AbstractBool.True: |
| - setReachable(cont); |
| - setValue(cont.parameters.single, input); |
| + setValue(node, input); |
| break; |
| case AbstractBool.False: |
| - break; // Cast fails. Continuation should remain unreachable. |
| + setValue(node, lattice.nothing); // Cast fails. |
| + break; |
| case AbstractBool.Maybe: |
| - setReachable(cont); |
| // Narrow type of output to those that survive the cast. |
| TypeMask type = input.type.intersection( |
| typeSystem.subtypesOf(node.dartType).nullable(), |
| classWorld); |
| - setValue(cont.parameters.single, nonConstant(type)); |
| + setValue(node, nonConstant(type)); |
| break; |
| } |
| } |
| @@ -3088,15 +2951,13 @@ class TypePropagationVisitor implements Visitor { |
| @override |
| void visitForeignCode(ForeignCode node) { |
| - if (node.continuation != null) { |
| - setResult(node, nonConstant(node.type)); |
| - } |
| + setValue(node, nonConstant(node.type)); |
| } |
| @override |
| void visitGetLength(GetLength node) { |
| AbstractValue input = getValue(node.object.definition); |
| - node.objectIsNotNull = getValue(node.object.definition).isDefinitelyNotNull; |
| + node.objectIsNotNull = input.isDefinitelyNotNull; |
| int length = typeSystem.getContainerLength(input.type); |
| if (length != null) { |
| // TODO(asgerf): Constant-folding the length might degrade the VM's |
| @@ -3125,7 +2986,7 @@ class TypePropagationVisitor implements Visitor { |
| @override |
| visitYield(Yield node) { |
| - setReachable(node.continuation.definition); |
| + setValue(node, nonConstant()); |
| } |
| @override |
| @@ -3215,16 +3076,6 @@ class AbstractValue { |
| } |
| } |
| -/// Enum-like class with the names of internal methods we care about. |
| -abstract class InternalMethod { |
| - static const String Stringify = 'S'; |
| -} |
| - |
| -/// Enum-like class with the names of dart:core methods we care about. |
| -abstract class CorelibMethod { |
| - static const String Identical = 'identical'; |
| -} |
| - |
| /// Suggested name for a synthesized loop index. |
| class LoopIndexEntity extends Entity { |
| String get name => 'i'; |