diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxedBasicValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxedBasicValue.java index 31564b1966a..87231ec352b 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxedBasicValue.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxedBasicValue.java @@ -16,6 +16,8 @@ package org.jetbrains.jet.codegen.optimization.boxing; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.AsmUtil; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; @@ -28,15 +30,18 @@ import java.util.Set; public class BoxedBasicValue extends BasicValue { private final Set associatedInsns = new HashSet(); + private final AbstractInsnNode boxingInsn; private final Set associatedVariables = new HashSet(); private final Set mergedWith = new HashSet(); private final Type primitiveType; + private final RangeIteratorBasicValue numberIterator; private boolean isSafeToRemove = true; - public BoxedBasicValue(Type boxedType, AbstractInsnNode insnNode) { + public BoxedBasicValue(Type boxedType, AbstractInsnNode boxingInsn, @Nullable RangeIteratorBasicValue numberIterator) { super(boxedType); this.primitiveType = AsmUtil.unboxType(boxedType); - associatedInsns.add(insnNode); + this.boxingInsn = boxingInsn; + this.numberIterator = numberIterator; } @Override @@ -111,4 +116,18 @@ public class BoxedBasicValue extends BasicValue { public boolean isDoubleSize() { return getPrimitiveType().getSize() == 2; } + + @NotNull + public AbstractInsnNode getBoxingInsn() { + return boxingInsn; + } + + public boolean isFromNumberIterator() { + return numberIterator != null; + } + + @Nullable + public RangeIteratorBasicValue getNumberIterator() { + return numberIterator; + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java index e730d0e64ad..812a1adab71 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/BoxingInterpreter.java @@ -35,6 +35,26 @@ import java.util.List; import java.util.Map; public class BoxingInterpreter extends OptimizationBasicInterpreter { + private static boolean isProgressionClass(String internalName) { + return internalName.startsWith("kotlin/") && ( + internalName.endsWith("Progression") || + internalName.endsWith("Range") + ); + } + + /** + * e.g. for "kotlin/IntRange" it returns "Int" + * + * @param progressionClassInternalName + * @return + */ + private static String getValuesTypePartOfProgressionClass(String progressionClassInternalName) { + progressionClassInternalName = progressionClassInternalName.substring("kotlin/".length()); + + int cutAtTheEnd = (progressionClassInternalName.endsWith("Progression")) ? "Progression".length() : "Range".length(); + return progressionClassInternalName.substring(0, progressionClassInternalName.length() - cutAtTheEnd); + } + private static final ImmutableSet wrappersClassNames; static { @@ -47,13 +67,6 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { wrappersClassNames = wrappersClassesBuilder.build(); } - private final Map boxingPlaces = new HashMap(); - private final InsnList insnList; - - BoxingInterpreter(InsnList insnList) { - this.insnList = insnList; - } - private static boolean isWrapperClassName(@NotNull String owner) { return wrappersClassNames.contains(owner); } @@ -86,27 +99,70 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { return isWrapperClassName(methodInsnNode.owner) && methodInsnNode.name.equals("valueOf"); } + private static boolean isIteratorMethodCallOfProgression( + @NotNull AbstractInsnNode insn, @NotNull List values + ) { + return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && + values.get(0).getType() != null && + isProgressionClass(values.get(0).getType().getInternalName()) && + ((MethodInsnNode) insn).name.equals("iterator")); + } + + private static boolean isNextMethodCallOfProgressionIterator( + @NotNull AbstractInsnNode insn, @NotNull List values + ) { + return (insn.getOpcode() == Opcodes.INVOKEINTERFACE && + values.get(0) instanceof RangeIteratorBasicValue && + ((MethodInsnNode) insn).name.equals("next")); + } + + private final Map boxingPlaces = new HashMap(); + private final InsnList insnList; + + BoxingInterpreter(InsnList insnList) { + this.insnList = insnList; + } + + @NotNull + private BoxedBasicValue createNewBoxing( + @NotNull AbstractInsnNode insn, @NotNull Type type, @Nullable RangeIteratorBasicValue numberIterator + ) { + int index = insnList.indexOf(insn); + if (!boxingPlaces.containsKey(index)) { + BoxedBasicValue boxedBasicValue = new BoxedBasicValue(type, insn, numberIterator); + onNewBoxedValue(boxedBasicValue); + boxingPlaces.put(index, boxedBasicValue); + } + + return boxingPlaces.get(index); + } + @Override @Nullable public BasicValue naryOperation(@NotNull AbstractInsnNode insn, @NotNull List values) throws AnalyzerException { BasicValue value = super.naryOperation(insn, values); if (isBoxing(insn)) { - int index = insnList.indexOf(insn); - if (!boxingPlaces.containsKey(index)) { - BoxedBasicValue boxedBasicValue = new BoxedBasicValue(value.getType(), insn); - onNewBoxedValue(boxedBasicValue); - boxingPlaces.put(index, boxedBasicValue); - } - - return boxingPlaces.get(index); + return createNewBoxing(insn, value.getType(), null); } else if (isUnboxing(insn) && values.get(0) instanceof BoxedBasicValue && value.getType().equals(((BoxedBasicValue) values.get(0)).getPrimitiveType())) { - onUnboxing((BoxedBasicValue) values.get(0), insn); } + else if (isIteratorMethodCallOfProgression(insn, values)) { + return new RangeIteratorBasicValue( + getValuesTypePartOfProgressionClass(values.get(0).getType().getInternalName()) + ); + } + else if (isNextMethodCallOfProgressionIterator(insn, values)) { + RangeIteratorBasicValue numberIterator = (RangeIteratorBasicValue) values.get(0); + return createNewBoxing( + insn, + AsmUtil.boxType(numberIterator.getValuesPrimitiveType()), + numberIterator + ); + } else { for (BasicValue arg : values) { if (arg instanceof BoxedBasicValue) { @@ -118,10 +174,15 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { return value; } + private static boolean isExactValue(@NotNull BasicValue value) { + return value instanceof RangeIteratorBasicValue || + value instanceof BoxedBasicValue || + (value.getType() != null && isProgressionClass(value.getType().getInternalName())); + } + @Override - @Nullable public BasicValue unaryOperation(@NotNull AbstractInsnNode insn, @NotNull BasicValue value) throws AnalyzerException { - if (insn.getOpcode() == Opcodes.CHECKCAST && value instanceof BoxedBasicValue) { + if (insn.getOpcode() == Opcodes.CHECKCAST && isExactValue(value)) { return value; } @@ -132,16 +193,16 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { @NotNull public BasicValue merge(@NotNull BasicValue v, @NotNull BasicValue w) { if (v instanceof BoxedBasicValue && ((BoxedBasicValue) v).typeEquals(w)) { - ((BoxedBasicValue) v).mergeWith((BoxedBasicValue) w); + onMergeSuccess((BoxedBasicValue) v, (BoxedBasicValue) w); return v; } - if (v instanceof BoxedBasicValue) { + if (v instanceof BoxedBasicValue && w != BasicValue.UNINITIALIZED_VALUE) { onMergeFail((BoxedBasicValue) v); v = new BasicValue(v.getType()); } - if (w instanceof BoxedBasicValue) { + if (w instanceof BoxedBasicValue && v != BasicValue.UNINITIALIZED_VALUE) { onMergeFail((BoxedBasicValue) w); w = new BasicValue(w.getType()); } @@ -149,7 +210,6 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { return super.merge(v, w); } - protected void onNewBoxedValue(@NotNull BoxedBasicValue value) { } @@ -165,4 +225,8 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter { protected void onMergeFail(@NotNull BoxedBasicValue value) { } + + protected void onMergeSuccess(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) { + + } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RangeIteratorBasicValue.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RangeIteratorBasicValue.java new file mode 100644 index 00000000000..71f095458c5 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RangeIteratorBasicValue.java @@ -0,0 +1,89 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen.optimization.boxing; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.org.objectweb.asm.Type; +import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue; + +public class RangeIteratorBasicValue extends BasicValue { + private static Type getValuesType(@NotNull String valuesTypeName) { + if (valuesTypeName.equals("Byte")) { + return Type.BYTE_TYPE; + } + if (valuesTypeName.equals("Char")) { + return Type.CHAR_TYPE; + } + if (valuesTypeName.equals("Short")) { + return Type.SHORT_TYPE; + } + if (valuesTypeName.equals("Int")) { + return Type.INT_TYPE; + } + if (valuesTypeName.equals("Long")) { + return Type.LONG_TYPE; + } + if (valuesTypeName.equals("Float")) { + return Type.FLOAT_TYPE; + } + if (valuesTypeName.equals("Double")) { + return Type.DOUBLE_TYPE; + } + + throw new RuntimeException("Unexpected type " + valuesTypeName); + } + + private final Type valuesPrimitiveType; + private final String valuesPrimitiveTypeName; + + public RangeIteratorBasicValue(@NotNull String valuesPrimitiveTypeName) { + super(Type.getObjectType("kotlin/" + valuesPrimitiveTypeName + "Iterator")); + this.valuesPrimitiveType = getValuesType(valuesPrimitiveTypeName); + this.valuesPrimitiveTypeName = valuesPrimitiveTypeName; + } + + public Type getValuesPrimitiveType() { + return valuesPrimitiveType; + } + + public String getValuesPrimitiveTypeName() { + return valuesPrimitiveTypeName; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + + RangeIteratorBasicValue value = (RangeIteratorBasicValue) o; + + if (!valuesPrimitiveType.equals(value.valuesPrimitiveType)) return false; + + return true; + } + + @NotNull + public String getNextMethodName() { + return "next" + getValuesPrimitiveTypeName(); + } + + @NotNull + public String getNextMethodDesc() { + return "()" + getValuesPrimitiveType().getDescriptor(); + } +} diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java index 8f6d4675586..c8836d3ad2a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingInterpreter.java @@ -138,6 +138,13 @@ class RedundantBoxingInterpreter extends BoxingInterpreter { markValueAsDirty(v); } + @Override + protected void onMergeSuccess( + @NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w + ) { + v.mergeWith(w); + } + @NotNull public Set getCandidatesBoxedValues() { return candidatesBoxedValues; diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java index ddaab67972d..ba1ddfbbaea 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java @@ -50,7 +50,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { adaptLocalVariableTableForBoxedValues(node, frames); - int[] remapping = variablesRemapping(valuesToOptimize, node); + int[] remapping = buildVariablesRemapping(valuesToOptimize, node); applyVariablesRemapping(node, remapping); adaptInstructionsForBoxedValues(node, frames, valuesToOptimize); @@ -206,7 +206,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { } @NotNull - private static int[] variablesRemapping(@NotNull Iterable values, @NotNull MethodNode node) { + private static int[] buildVariablesRemapping(@NotNull Iterable values, @NotNull MethodNode node) { Set longTypesVars = new HashSet(); for (BoxedBasicValue value : values) { if (value.getPrimitiveType().getSize() == 2) { @@ -271,11 +271,41 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { } private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedBasicValue value) { + adaptBoxingInstruction(node, value); + for (AbstractInsnNode insn : value.getAssociatedInsns()) { adaptInstruction(node, insn, value); } } + private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedBasicValue value) { + if (!value.isFromNumberIterator()) { + node.instructions.remove(value.getBoxingInsn()); + } + else { + RangeIteratorBasicValue iterator = value.getNumberIterator(); + assert iterator != null : "iterator should not be null because isFromNumberIterator returns true"; + + //add checkcast to kotlin/Iterator before next() call + node.instructions.insertBefore( + value.getBoxingInsn(), + new TypeInsnNode(Opcodes.CHECKCAST, iterator.getType().getInternalName()) + ); + + //invoke concrete method (kotlin/iteraror.next()) + node.instructions.set( + value.getBoxingInsn(), + new MethodInsnNode( + Opcodes.INVOKEVIRTUAL, + iterator.getType().getInternalName(), + iterator.getNextMethodName(), + iterator.getNextMethodDesc(), + false + ) + ); + } + } + private static void adaptInstruction( @NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value ) {