Chromium Code Reviews| Index: sdk/lib/_internal/compiler/implementation/ssa/optimize.dart |
| =================================================================== |
| --- sdk/lib/_internal/compiler/implementation/ssa/optimize.dart (revision 17895) |
| +++ sdk/lib/_internal/compiler/implementation/ssa/optimize.dart (working copy) |
| @@ -145,15 +145,8 @@ |
| while (instruction != null) { |
| HInstruction next = instruction.next; |
| HInstruction replacement = instruction.accept(this); |
| - if (!identical(replacement, instruction)) { |
| - if (!replacement.isInBasicBlock()) { |
| - // The constant folding can return an instruction that is already |
| - // part of the graph (like an input), so we only add the replacement |
| - // if necessary. |
| - block.addAfter(instruction, replacement); |
| - } |
| + if (replacement != instruction) { |
| block.rewrite(instruction, replacement); |
| - block.remove(instruction); |
| // If we can replace [instruction] with [replacement], then |
| // [replacement]'s type can be narrowed. |
| @@ -169,6 +162,16 @@ |
| if (replacement.sourcePosition == null) { |
| replacement.sourcePosition = instruction.sourcePosition; |
| } |
| + if (!replacement.isInBasicBlock()) { |
| + // The constant folding can return an instruction that is already |
| + // part of the graph (like an input), so we only add the replacement |
| + // if necessary. |
| + block.addAfter(instruction, replacement); |
| + // Visit the replacement as the next instruction in case it |
| + // can also be constant folded away. |
| + next = replacement; |
| + } |
| + block.remove(instruction); |
| } |
| instruction = next; |
| } |
| @@ -220,16 +223,44 @@ |
| return null; |
| } |
| + HInstruction optimizeLengthInterceptedGetter(HInvokeDynamic node) { |
| + HInstruction actualReceiver = node.inputs[1]; |
| + if (actualReceiver.isIndexablePrimitive(types)) { |
| + if (actualReceiver.isConstantString()) { |
| + HConstant constantInput = actualReceiver; |
| + StringConstant constant = constantInput.constant; |
| + return graph.addConstantInt(constant.length, constantSystem); |
| + } else if (actualReceiver.isConstantList()) { |
| + HConstant constantInput = actualReceiver; |
| + ListConstant constant = constantInput.constant; |
| + return graph.addConstantInt(constant.length, constantSystem); |
| + } |
| + Element element; |
| + bool isAssignable; |
| + if (actualReceiver.isString(types)) { |
| + element = backend.jsStringLength; |
| + isAssignable = false; |
| + } else { |
| + element = backend.jsArrayLength; |
| + isAssignable = !actualReceiver.isFixedArray(types); |
| + } |
| + HFieldGet result = new HFieldGet( |
| + element, actualReceiver, isAssignable: isAssignable); |
| + result.guaranteedType = HType.INTEGER; |
| + types[result] = HType.INTEGER; |
| + return result; |
| + } else if (actualReceiver.isConstantMap()) { |
| + HConstant constantInput = actualReceiver; |
| + MapConstant constant = constantInput.constant; |
| + return graph.addConstantInt(constant.length, constantSystem); |
| + } |
| + return node; |
| + } |
| + |
| HInstruction handleInterceptorCall(HInvokeDynamic node) { |
| // We only optimize for intercepted method calls in this method. |
| - if (node.selector.isGetter() || node.selector.isSetter()) return node; |
| + Selector selector = node.selector; |
| - HInstruction input = node.inputs[1]; |
| - if (input.isString(types) |
| - && node.selector.name == const SourceString('toString')) { |
| - return node.inputs[1]; |
| - } |
| - |
| // Try constant folding the instruction. |
| Operation operation = node.specializer.operation(constantSystem); |
| if (operation != null) { |
| @@ -245,6 +276,7 @@ |
| if (instruction != null) return instruction; |
| // Check if this call does not need to be intercepted. |
| + HInstruction input = node.inputs[1]; |
| HType type = types[input]; |
| var interceptor = node.inputs[0]; |
| if (interceptor is !HThis && !type.canBePrimitive()) { |
| @@ -260,43 +292,58 @@ |
| } |
| if (interceptedClasses.contains(compiler.objectClass)) return node; |
| } |
| - // Change the call to a regular invoke dynamic call. |
| - return new HInvokeDynamicMethod( |
| - node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| + if (selector.isGetter()) { |
| + // Change the call to a regular invoke dynamic call. |
| + return new HInvokeDynamicGetter(selector, null, input, false); |
| + } else if (selector.isSetter()) { |
| + return new HInvokeDynamicSetter( |
| + selector, null, input, node.inputs[2], false); |
| + } else { |
| + // Change the call to a regular invoke dynamic call. |
| + return new HInvokeDynamicMethod( |
| + selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| + } |
| } |
| - Selector selector = node.selector; |
| SourceString selectorName = selector.name; |
| - Element target; |
| - if (input.isExtendableArray(types)) { |
| - if (selectorName == backend.jsArrayRemoveLast.name |
| - && selector.argumentCount == 0) { |
| - target = backend.jsArrayRemoveLast; |
| - } else if (selectorName == backend.jsArrayAdd.name |
| - && selector.argumentCount == 1 |
| - && selector.namedArgumentCount == 0 |
| - && !compiler.enableTypeAssertions) { |
| - target = backend.jsArrayAdd; |
| + if (selector.isCall()) { |
| + Element target; |
| + if (input.isExtendableArray(types)) { |
| + if (selectorName == backend.jsArrayRemoveLast.name |
| + && selector.argumentCount == 0) { |
| + target = backend.jsArrayRemoveLast; |
| + } else if (selectorName == backend.jsArrayAdd.name |
|
kasperl
2013/02/01 16:29:51
Maybe we could store some object in the backend th
ngeoffray
2013/02/04 13:31:30
Actually, we're kind of doing a Selector.applies c
|
| + && selector.argumentCount == 1 |
| + && selector.namedArgumentCount == 0 |
| + && !compiler.enableTypeAssertions) { |
| + target = backend.jsArrayAdd; |
| + } |
| + } else if (input.isString(types)) { |
| + if (selectorName == backend.jsStringSplit.name |
| + && selector.argumentCount == 1 |
| + && selector.namedArgumentCount == 0 |
| + && node.inputs[2].isString(types)) { |
| + target = backend.jsStringSplit; |
| + } else if (selectorName == backend.jsStringConcat.name |
| + && selector.argumentCount == 1 |
| + && selector.namedArgumentCount == 0 |
| + && node.inputs[2].isString(types)) { |
| + target = backend.jsStringConcat; |
| + } else if (selectorName == const SourceString('toString') |
| + && selector.argumentCount == 0) { |
| + return node.inputs[1]; |
| + } |
| } |
| - } else if (input.isString(types)) { |
| - if (selectorName == backend.jsStringSplit.name |
| - && selector.argumentCount == 1 |
| - && selector.namedArgumentCount == 0 |
| - && node.inputs[2].isString(types)) { |
| - target = backend.jsStringSplit; |
| - } else if (selectorName == backend.jsStringConcat.name |
| - && selector.argumentCount == 1 |
| - && selector.namedArgumentCount == 0 |
| - && node.inputs[2].isString(types)) { |
| - target = backend.jsStringConcat; |
| + if (target != null) { |
| + HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| + node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| + result.element = target; |
| + return result; |
| } |
| + } else if (selector.isGetter() |
| + && selectorName == const SourceString("length")) { |
| + return optimizeLengthInterceptedGetter(node); |
| } |
| - if (target != null) { |
| - HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| - node.selector, node.inputs.getRange(1, node.inputs.length - 1)); |
| - result.element = target; |
| - return result; |
| - } |
| return node; |
| } |
| @@ -345,29 +392,6 @@ |
| return node; |
| } |
| - /** |
| - * Turns a primitive instruction (e.g. [HIndex], [HAdd], ...) into a |
| - * [HInvokeDynamic] because we know the receiver is not a JS |
| - * primitive object. |
| - */ |
| - HInstruction fromPrimitiveInstructionToDynamicInvocation(HInstruction node, |
| - Selector selector) { |
| - HBoundedType type = types[node.inputs[1]]; |
| - HInvokeDynamicMethod result = new HInvokeDynamicMethod( |
| - selector, |
| - node.inputs.getRange(1, node.inputs.length - 1)); |
| - if (type.isExact()) { |
| - HBoundedType concrete = type; |
| - // TODO(johnniwinther): Add lookup by selector to HBoundedType. |
| - Element element = concrete.lookupMember(selector.name); |
| - if (selector.applies(element, compiler)) { |
| - // The target is only valid if the selector applies. |
| - result.element = element; |
| - } |
| - } |
| - return result; |
| - } |
| - |
| HInstruction visitIntegerCheck(HIntegerCheck node) { |
| HInstruction value = node.value; |
| if (value.isInteger(types)) return value; |
| @@ -584,45 +608,8 @@ |
| return node; |
| } |
| - HInstruction optimizeLengthInterceptedCall(HInvokeDynamicGetter node) { |
| - HInstruction actualReceiver = node.inputs[1]; |
| - if (actualReceiver.isIndexablePrimitive(types)) { |
| - if (actualReceiver.isConstantString()) { |
| - HConstant constantInput = actualReceiver; |
| - StringConstant constant = constantInput.constant; |
| - return graph.addConstantInt(constant.length, constantSystem); |
| - } else if (actualReceiver.isConstantList()) { |
| - HConstant constantInput = actualReceiver; |
| - ListConstant constant = constantInput.constant; |
| - return graph.addConstantInt(constant.length, constantSystem); |
| - } |
| - Element element; |
| - bool isAssignable; |
| - if (actualReceiver.isString(types)) { |
| - element = backend.jsStringLength; |
| - isAssignable = false; |
| - } else { |
| - element = backend.jsArrayLength; |
| - isAssignable = !actualReceiver.isFixedArray(types); |
| - } |
| - HFieldGet result = new HFieldGet( |
| - element, actualReceiver, isAssignable: isAssignable); |
| - result.guaranteedType = HType.INTEGER; |
| - types[result] = HType.INTEGER; |
| - return result; |
| - } else if (actualReceiver.isConstantMap()) { |
| - HConstant constantInput = actualReceiver; |
| - MapConstant constant = constantInput.constant; |
| - return graph.addConstantInt(constant.length, constantSystem); |
| - } |
| - return node; |
| - } |
| - |
| HInstruction visitInvokeDynamicGetter(HInvokeDynamicGetter node) { |
| - if (node.selector.name == const SourceString('length') |
| - && node.isInterceptorCall) { |
| - return optimizeLengthInterceptedCall(node); |
| - } |
| + if (node.isInterceptorCall) return handleInterceptorCall(node); |
| Element field = |
| findConcreteFieldForDynamicAccess(node.receiver, node.selector); |
| @@ -647,6 +634,8 @@ |
| } |
| HInstruction visitInvokeDynamicSetter(HInvokeDynamicSetter node) { |
| + if (node.isInterceptorCall) return handleInterceptorCall(node); |
| + |
| Element field = |
| findConcreteFieldForDynamicAccess(node.receiver, node.selector); |
| if (field == null || !field.isAssignable()) return node; |