dealing with ranges

This commit is contained in:
Denis Zharkov
2014-07-04 17:07:38 +04:00
committed by Alexander Udalov
parent ee87b11723
commit ed445f6488
5 changed files with 235 additions and 26 deletions
@@ -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<AbstractInsnNode> associatedInsns = new HashSet<AbstractInsnNode>();
private final AbstractInsnNode boxingInsn;
private final Set<Integer> associatedVariables = new HashSet<Integer>();
private final Set<BoxedBasicValue> mergedWith = new HashSet<BoxedBasicValue>();
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;
}
}
@@ -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<String> wrappersClassNames;
static {
@@ -47,13 +67,6 @@ public class BoxingInterpreter extends OptimizationBasicInterpreter {
wrappersClassNames = wrappersClassesBuilder.build();
}
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
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<? extends BasicValue> 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<? extends BasicValue> values
) {
return (insn.getOpcode() == Opcodes.INVOKEINTERFACE &&
values.get(0) instanceof RangeIteratorBasicValue &&
((MethodInsnNode) insn).name.equals("next"));
}
private final Map<Integer, BoxedBasicValue> boxingPlaces = new HashMap<Integer, BoxedBasicValue>();
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<? extends BasicValue> 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) {
}
}
@@ -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();
}
}
@@ -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<BoxedBasicValue> getCandidatesBoxedValues() {
return candidatesBoxedValues;
@@ -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<BoxedBasicValue> values, @NotNull MethodNode node) {
private static int[] buildVariablesRemapping(@NotNull Iterable<BoxedBasicValue> values, @NotNull MethodNode node) {
Set<Integer> longTypesVars = new HashSet<Integer>();
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/<T>Iterator before next() call
node.instructions.insertBefore(
value.getBoxingInsn(),
new TypeInsnNode(Opcodes.CHECKCAST, iterator.getType().getInternalName())
);
//invoke concrete method (kotlin/<T>iteraror.next<T>())
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
) {