diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxedBasicValue.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxedBasicValue.kt index e3126c94ee8..1c5c3ffb29d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxedBasicValue.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxedBasicValue.kt @@ -20,35 +20,53 @@ import com.intellij.openapi.util.Pair import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue import org.jetbrains.kotlin.resolve.jvm.AsmTypes +import org.jetbrains.kotlin.utils.toReadOnlyList import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import java.util.* -class BoxedBasicValue( +abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) { + abstract val descriptor: BoxedValueDescriptor + abstract fun taint(): BoxedBasicValue + + override fun equals(other: Any?) = this === other + override fun hashCode() = System.identityHashCode(this) +} + + +class CleanBoxedValue( boxedType: Type, + boxingInsn: AbstractInsnNode, + progressionIterator: ProgressionIteratorBasicValue? +) : BoxedBasicValue(boxedType) { + override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator) + + private var tainted: TaintedBoxedValue? = null + override fun taint(): BoxedBasicValue = tainted ?: TaintedBoxedValue(this).also { tainted = it } +} + + +class TaintedBoxedValue(val boxedBasicValue: CleanBoxedValue) : BoxedBasicValue(boxedBasicValue.type) { + override val descriptor get() = boxedBasicValue.descriptor + + override fun taint(): BoxedBasicValue = this +} + + +class BoxedValueDescriptor( + val boxedType: Type, val boxingInsn: AbstractInsnNode, val progressionIterator: ProgressionIteratorBasicValue? -) : StrictBasicValue(boxedType) { +) { private val associatedInsns = HashSet() private val unboxingWithCastInsns = HashSet>() private val associatedVariables = HashSet() - private val mergedWith = HashSet() + private val mergedWith = HashSet() - val primitiveType: Type = unboxType(boxedType) var isSafeToRemove = true; private set + val unboxedType: Type = getUnboxedType(boxedType) - override fun equals(other: Any?) = - this === other - - fun typeEquals(other: BasicValue) = - other is BoxedBasicValue && type == other.type - - override fun hashCode() = - System.identityHashCode(this) - - fun getAssociatedInsns(): List = - ArrayList(associatedInsns) + fun getAssociatedInsns() = associatedInsns.toReadOnlyList() fun addInsn(insnNode: AbstractInsnNode) { associatedInsns.add(insnNode) @@ -61,22 +79,20 @@ class BoxedBasicValue( fun getVariablesIndexes(): List = ArrayList(associatedVariables) - fun addMergedWith(value: BoxedBasicValue) { - mergedWith.add(value) + fun addMergedWith(descriptor: BoxedValueDescriptor) { + mergedWith.add(descriptor) } - fun getMergedWith(): Iterable = + fun getMergedWith(): Iterable = mergedWith fun markAsUnsafeToRemove() { isSafeToRemove = false } - fun isDoubleSize() = - primitiveType.size == 2 + fun isDoubleSize() = unboxedType.size == 2 - fun isFromProgressionIterator() = - progressionIterator != null + fun isFromProgressionIterator() = progressionIterator != null fun addUnboxingWithCastTo(insn: AbstractInsnNode, type: Type) { unboxingWithCastInsns.add(Pair.create(insn, type)) @@ -84,15 +100,14 @@ class BoxedBasicValue( fun getUnboxingWithCastInsns(): Set> = unboxingWithCastInsns - - companion object { - private fun unboxType(boxedType: Type): Type { - val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType) - if (primitiveType != null) return primitiveType - - if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE - - throw IllegalArgumentException("Expected primitive type wrapper or KClass, got: $boxedType") - } - } +} + + +fun getUnboxedType(boxedType: Type): Type { + val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType) + if (primitiveType != null) return primitiveType + + if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE + + throw IllegalArgumentException("Expected primitive type wrapper or KClass, got: $boxedType") } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt index 34724925786..2bc100b0112 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/BoxingInterpreter.kt @@ -29,7 +29,6 @@ import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.InsnList import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode -import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import java.util.* @@ -39,14 +38,23 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue { val index = insnList.indexOf(insn) return boxingPlaces.getOrPut(index) { - val boxedBasicValue = BoxedBasicValue(type, insn, progressionIterator) + val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator) onNewBoxedValue(boxedBasicValue) boxedBasicValue } } - @Throws(AnalyzerException::class) + protected fun checkUsedValue(value: BasicValue) { + if (value is TaintedBoxedValue) { + onMergeFail(value) + } + } + override fun naryOperation(insn: AbstractInsnNode, values: List): BasicValue? { + values.forEach { + checkUsedValue(it) + } + val value = super.naryOperation(insn, values) val firstArg = values.firstOrNull() ?: return value @@ -80,36 +88,39 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic } } - @Throws(AnalyzerException::class) - override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? = - if (insn.opcode == Opcodes.CHECKCAST && isExactValue(value)) - value - else - super.unaryOperation(insn, value) + override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? { + checkUsedValue(value) + + return if (insn.opcode == Opcodes.CHECKCAST && isExactValue(value)) + value + else + super.unaryOperation(insn, value) + } protected open fun isExactValue(value: BasicValue) = value is ProgressionIteratorBasicValue || - value is BoxedBasicValue || + value is CleanBoxedValue || value.type != null && isProgressionClass(value.type.internalName) override fun merge(v: BasicValue, w: BasicValue) = when { - v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE -> { + v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE -> StrictBasicValue.UNINITIALIZED_VALUE - } - v is BoxedBasicValue && v.typeEquals(w) -> { - onMergeSuccess(v, w as BoxedBasicValue) - v - } - else -> { - if (v is BoxedBasicValue) { - onMergeFail(v) - } - if (w is BoxedBasicValue) { - onMergeFail(w) + v is BoxedBasicValue && w is BoxedBasicValue -> { + onMergeSuccess(v, w) + when { + v is TaintedBoxedValue -> v + w is TaintedBoxedValue -> w + v.type != w.type -> v.taint() + else -> v } + } + v is BoxedBasicValue -> + v.taint() + w is BoxedBasicValue -> + w.taint() + else -> super.merge(v, w) - } } protected open fun onNewBoxedValue(value: BoxedBasicValue) {} @@ -197,7 +208,5 @@ private fun isProgressionClass(internalClassName: String) = RangeCodegenUtil.isRangeOrProgression(buildFqNameByInternal(internalClassName)) private fun getValuesTypeOfProgressionClass(progressionClassInternalName: String) = - RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName))?.let { - type -> - type.typeName.asString() - } ?: error("type should be not null") + RangeCodegenUtil.getPrimitiveRangeOrProgressionElementType(buildFqNameByInternal(progressionClassInternalName)) + ?.typeName?.asString() ?: error("type should be not null") diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxedValuesCollection.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxedValuesCollection.java index 5849a988d13..848062dfd7b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxedValuesCollection.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxedValuesCollection.java @@ -22,25 +22,25 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Set; -public class RedundantBoxedValuesCollection implements Iterable { - private final Set safeToDeleteValues = new HashSet(); +public class RedundantBoxedValuesCollection implements Iterable { + private final Set safeToDeleteValues = new HashSet(); - public void add(@NotNull BoxedBasicValue value) { - safeToDeleteValues.add(value); + public void add(@NotNull BoxedValueDescriptor descriptor) { + safeToDeleteValues.add(descriptor); } - public void remove(@NotNull BoxedBasicValue value) { - if (safeToDeleteValues.contains(value)) { - safeToDeleteValues.remove(value); - value.markAsUnsafeToRemove(); + public void remove(@NotNull BoxedValueDescriptor descriptor) { + if (safeToDeleteValues.contains(descriptor)) { + safeToDeleteValues.remove(descriptor); + descriptor.markAsUnsafeToRemove(); - for (BoxedBasicValue mergedValue : value.getMergedWith()) { - remove(mergedValue); + for (BoxedValueDescriptor mergedValueDescriptor : descriptor.getMergedWith()) { + remove(mergedValueDescriptor); } } } - public void merge(@NotNull BoxedBasicValue v, @NotNull BoxedBasicValue w) { + public void merge(@NotNull BoxedValueDescriptor v, @NotNull BoxedValueDescriptor w) { v.addMergedWith(w); w.addMergedWith(v); @@ -59,7 +59,7 @@ public class RedundantBoxedValuesCollection implements Iterable @NotNull @Override - public Iterator iterator() { + public Iterator iterator() { return safeToDeleteValues.iterator(); } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingInterpreter.kt index cbd0c7f3f03..1fe2de1a1ab 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingInterpreter.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingInterpreter.kt @@ -23,30 +23,12 @@ import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.InsnList import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode import org.jetbrains.org.objectweb.asm.tree.VarInsnNode -import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) { val candidatesBoxedValues = RedundantBoxedValuesCollection() - @Throws(AnalyzerException::class) - override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? { - processOperationWithBoxedValue(value1, insn) - processOperationWithBoxedValue(value2, insn) - - return super.binaryOperation(insn, value1, value2) - } - - @Throws(AnalyzerException::class) - override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? { - // in a valid code only aastore could happen with boxed value - processOperationWithBoxedValue(value3, insn) - - return super.ternaryOperation(insn, value1, value2, value3) - } - - @Throws(AnalyzerException::class) override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? { if ((insn.opcode == Opcodes.CHECKCAST || insn.opcode == Opcodes.INSTANCEOF) && value is BoxedBasicValue) { val typeInsn = insn as TypeInsnNode @@ -61,10 +43,23 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete return super.unaryOperation(insn, value) } - @Throws(AnalyzerException::class) + override fun binaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue): BasicValue? { + processOperationWithBoxedValue(value1, insn) + processOperationWithBoxedValue(value2, insn) + + return super.binaryOperation(insn, value1, value2) + } + + override fun ternaryOperation(insn: AbstractInsnNode, value1: BasicValue, value2: BasicValue, value3: BasicValue): BasicValue? { + // in a valid code only aastore could happen with boxed value + processOperationWithBoxedValue(value3, insn) + + return super.ternaryOperation(insn, value1, value2, value3) + } + override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue { - if (value is BoxedBasicValue && insn.opcode === Opcodes.ASTORE) { - value.addVariableIndex((insn as VarInsnNode).`var`) + if (value is BoxedBasicValue && insn.opcode == Opcodes.ASTORE) { + value.descriptor.addVariableIndex((insn as VarInsnNode).`var`) } processOperationWithBoxedValue(value, insn) @@ -77,15 +72,15 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete } override fun onNewBoxedValue(value: BoxedBasicValue) { - candidatesBoxedValues.add(value) + candidatesBoxedValues.add(value.descriptor) } override fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) { - if (value.primitiveType == resultType) { - addAssociatedInsn(value, insn) - } - else { - value.addUnboxingWithCastTo(insn, resultType) + value.descriptor.run { + if (unboxedType == resultType) + addAssociatedInsn(value, insn) + else + addUnboxingWithCastTo(insn, resultType) } } @@ -98,11 +93,13 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete } override fun onMergeSuccess(v: BoxedBasicValue, w: BoxedBasicValue) { - candidatesBoxedValues.merge(v, w) + candidatesBoxedValues.merge(v.descriptor, w.descriptor) } private fun processOperationWithBoxedValue(value: BasicValue?, insnNode: AbstractInsnNode) { if (value is BoxedBasicValue) { + checkUsedValue(value) + if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) { markValueAsDirty(value) } @@ -113,7 +110,7 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete } private fun markValueAsDirty(value: BoxedBasicValue) { - candidatesBoxedValues.remove(value) + candidatesBoxedValues.remove(value.descriptor) } companion object { @@ -127,17 +124,15 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete when (targetInternalName) { Type.getInternalName(Any::class.java) -> true - Type.getInternalName(Number::class.java) -> { - PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains( - value.primitiveType.sort) - } + Type.getInternalName(Number::class.java) -> + PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort) else -> - value.type.internalName.equals(targetInternalName) + value.type.internalName == targetInternalName } private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) { - if (value.isSafeToRemove) { - value.addInsn(insn) + value.descriptor.run { + if (isSafeToRemove) addInsn(insn) } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java index 6eb2023ea4b..6b4e65dc088 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.java @@ -16,8 +16,6 @@ package org.jetbrains.kotlin.codegen.optimization.boxing; -import com.google.common.base.Predicate; -import com.google.common.collect.Collections2; import com.intellij.openapi.util.Pair; import kotlin.collections.CollectionsKt; import kotlin.jvm.functions.Function1; @@ -38,9 +36,8 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { @Override public void transform(@NotNull String internalClassName, @NotNull MethodNode node) { RedundantBoxingInterpreter interpreter = new RedundantBoxingInterpreter(node.instructions); - Frame[] frames = analyze( - internalClassName, node, interpreter - ); + Frame[] frames = analyze(internalClassName, node, interpreter); + interpretPopInstructionsForBoxedValues(interpreter, node, frames); RedundantBoxedValuesCollection valuesToOptimize = interpreter.getCandidatesBoxedValues(); @@ -99,32 +96,25 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { continue; } - List usedValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames); + List variableValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames); - Collection boxed = Collections2.filter(usedValues, new Predicate() { + Collection boxed = CollectionsKt.filter(variableValues, new Function1() { @Override - public boolean apply(BasicValue input) { - return input instanceof BoxedBasicValue; + public Boolean invoke(BasicValue value) { + return value instanceof BoxedBasicValue; } }); if (boxed.isEmpty()) continue; - final BoxedBasicValue firstBoxed = (BoxedBasicValue) boxed.iterator().next(); + BoxedValueDescriptor firstBoxed = ((BoxedBasicValue) boxed.iterator().next()).getDescriptor(); + if (isUnsafeToRemoveBoxingForConnectedValues(variableValues, firstBoxed.getUnboxedType())) { + for (BasicValue value : variableValues) { + if (!(value instanceof BoxedBasicValue)) continue; - if (CollectionsKt.any(usedValues, new Function1() { - @Override - public Boolean invoke(BasicValue input) { - if (input == StrictBasicValue.UNINITIALIZED_VALUE) return false; - return input == null || - !(input instanceof BoxedBasicValue) || - !((BoxedBasicValue) input).isSafeToRemove() || - !((BoxedBasicValue) input).getPrimitiveType().equals(firstBoxed.getPrimitiveType()); - } - })) { - for (BasicValue value : usedValues) { - if (value instanceof BoxedBasicValue && ((BoxedBasicValue) value).isSafeToRemove()) { - values.remove((BoxedBasicValue) value); + BoxedValueDescriptor descriptor = ((BoxedBasicValue) value).getDescriptor(); + if (descriptor.isSafeToRemove()) { + values.remove(descriptor); needToRepeat = true; } } @@ -134,6 +124,20 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { return needToRepeat; } + private static boolean isUnsafeToRemoveBoxingForConnectedValues(List usedValues, final Type unboxedType) { + return CollectionsKt.any(usedValues, new Function1() { + @Override + public Boolean invoke(BasicValue input) { + if (input == StrictBasicValue.UNINITIALIZED_VALUE) return false; + if (!(input instanceof BoxedBasicValue)) return true; + + BoxedValueDescriptor descriptor = ((BoxedBasicValue) input).getDescriptor(); + return !descriptor.isSafeToRemove() || + !(descriptor.getUnboxedType().equals(unboxedType)); + } + }); + } + private static void adaptLocalVariableTableForBoxedValues(@NotNull MethodNode node, @NotNull Frame[] frames) { for (LocalVariableNode localVariableNode : node.localVariables) { if (Type.getType(localVariableNode.desc).getSort() != Type.OBJECT) { @@ -141,8 +145,11 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { } for (BasicValue value : getValuesStoredOrLoadedToVariable(localVariableNode, node, frames)) { - if (value == null || !(value instanceof BoxedBasicValue) || !((BoxedBasicValue) value).isSafeToRemove()) continue; - localVariableNode.desc = ((BoxedBasicValue) value).getPrimitiveType().getDescriptor(); + if (!(value instanceof BoxedBasicValue)) continue; + + BoxedValueDescriptor descriptor = ((BoxedBasicValue) value).getDescriptor(); + if (!descriptor.isSafeToRemove()) continue; + localVariableNode.desc = descriptor.getUnboxedType().getDescriptor(); } } } @@ -193,9 +200,9 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { @NotNull private static int[] buildVariablesRemapping(@NotNull RedundantBoxedValuesCollection values, @NotNull MethodNode node) { Set doubleSizedVars = new HashSet(); - for (BoxedBasicValue value : values) { - if (value.getPrimitiveType().getSize() == 2) { - doubleSizedVars.addAll(value.getVariablesIndexes()); + for (BoxedValueDescriptor valueDescriptor : values) { + if (valueDescriptor.isDoubleSize()) { + doubleSizedVars.addAll(valueDescriptor.getVariablesIndexes()); } } @@ -233,12 +240,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { @NotNull MethodNode node, @NotNull RedundantBoxedValuesCollection values ) { - for (BoxedBasicValue value : values) { + for (BoxedValueDescriptor value : values) { adaptInstructionsForBoxedValue(node, value); } } - private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedBasicValue value) { + private static void adaptInstructionsForBoxedValue(@NotNull MethodNode node, @NotNull BoxedValueDescriptor value) { adaptBoxingInstruction(node, value); for (Pair cast : value.getUnboxingWithCastInsns()) { @@ -250,7 +257,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { } } - private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedBasicValue value) { + private static void adaptBoxingInstruction(@NotNull MethodNode node, @NotNull BoxedValueDescriptor value) { if (!value.isFromProgressionIterator()) { node.instructions.remove(value.getBoxingInsn()); } @@ -280,12 +287,12 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { private static void adaptCastInstruction( @NotNull MethodNode node, - @NotNull BoxedBasicValue value, + @NotNull BoxedValueDescriptor value, @NotNull Pair castWithType ) { AbstractInsnNode castInsn = castWithType.getFirst(); MethodNode castInsnsListener = new MethodNode(Opcodes.ASM5); - new InstructionAdapter(castInsnsListener).cast(value.getPrimitiveType(), castWithType.getSecond()); + new InstructionAdapter(castInsnsListener).cast(value.getUnboxedType(), castWithType.getSecond()); for (AbstractInsnNode insn : castInsnsListener.instructions.toArray()) { node.instructions.insertBefore(castInsn, insn); @@ -295,7 +302,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { } private static void adaptInstruction( - @NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedBasicValue value + @NotNull MethodNode node, @NotNull AbstractInsnNode insn, @NotNull BoxedValueDescriptor value ) { boolean isDoubleSize = value.isDoubleSize(); @@ -322,7 +329,7 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer { node.instructions.set( insn, new VarInsnNode( - value.getPrimitiveType().getOpcode(intVarOpcode), + value.getUnboxedType().getOpcode(intVarOpcode), ((VarInsnNode) insn).var ) ); diff --git a/compiler/testData/codegen/box/boxingOptimization/taintedValues.kt b/compiler/testData/codegen/box/boxingOptimization/taintedValues.kt new file mode 100644 index 00000000000..83ce3905b87 --- /dev/null +++ b/compiler/testData/codegen/box/boxingOptimization/taintedValues.kt @@ -0,0 +1,11 @@ +// WITH_RUNTIME + +// Just make sure there's no VerifyError + +fun getOrElse() = + mapOf().getOrElse("foo") { 3 } + +fun isNotEmpty(l: ArrayList) = + l.iterator()?.hasNext() ?: false + +fun box() = "OK" \ No newline at end of file diff --git a/compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt b/compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt new file mode 100644 index 00000000000..5ea75e4adf6 --- /dev/null +++ b/compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt @@ -0,0 +1,49 @@ +// WITH_RUNTIME + +inline fun put( + x: T, + maxExclusive: Int, + isEmpty: (Int) -> Boolean, + equals: (T, T) -> Boolean, + fetch: (Int) -> T, + store: (Int, T) -> Unit +): Boolean { + var i = 0 + do { + if (isEmpty(i)) { + store(i, x) + return true + } + + val y = fetch(i) + if (equals(x, y)) { + return false + } + + i++ + if (i >= maxExclusive) return false + } while (true) +} + +const val SIZE = 16 +val arr = IntArray(SIZE) { -1 } + +fun putNonNegInt(x: Int) = + put(x, SIZE, + isEmpty = { arr[it] < 0 }, + equals = { x, y -> x == y }, + fetch = { arr[it] }, + store = { i, x -> arr[i] = x } + ) + +fun box(): String { + putNonNegInt(1) + putNonNegInt(2) + putNonNegInt(3) + + if (arr[0] != 1) return "Fail, ${arr.toList().toString()}" + if (arr[1] != 2) return "Fail, ${arr.toList().toString()}" + if (arr[2] != 3) return "Fail, ${arr.toList().toString()}" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862.kt new file mode 100644 index 00000000000..194a8e1e572 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862.kt @@ -0,0 +1,58 @@ +// FILE: test.kt + +// @TestKt.class: +// 0 valueOf +// 0 Value\s\(\) + +val mask = 127 +val entries = IntArray(128) +val flags = BooleanArray(128) + +fun distance(index: Int, hash: Int): Int = (128 + index - (hash and mask)) and mask + +fun insertSad(x: Int): Boolean { + return insertWithBoxing( + x, + hash = { it }, + equals = { a, b -> a == b }, + isEmpty = { !flags[it] }, + fetch = { entries[it] }, + store = { i, x -> entries[i] = x; flags[i] = true; } + ) +} + +// FILE: inline.kt +inline fun insertWithBoxing(entry: T, + hash: (T) -> Int, + equals: (T, T) -> Boolean, + isEmpty: (Int) -> Boolean, + fetch: (Int) -> T, + store: (Int, T) -> Unit): Boolean { + var currentEntry = entry + var index = hash(entry) and mask + var dist = 0 + do { + if (isEmpty(index)) { + store(index, currentEntry) + return true + } + + val existingEntry = fetch(index) + if (equals(existingEntry, currentEntry)) { + return false + } + + val existingHash = hash(existingEntry) + val existingDistance = distance(index, existingHash) + if (existingDistance < dist) { + store(index, currentEntry) + currentEntry = existingEntry + dist = existingDistance + } + + dist += 1 + index = (index + 1) and mask + } + while (true) +} + diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862_2.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862_2.kt new file mode 100644 index 00000000000..ef75b3c92ba --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862_2.kt @@ -0,0 +1,43 @@ +// FILE: test.kt + +// @TestKt.class: +// 0 valueOf +// 0 Value\s\(\) + +const val SIZE = 16 +val arr = IntArray(SIZE) { -1 } + +fun putNonNegInt(x: Int) = + put(x, SIZE, + isEmpty = { arr[it] < 0 }, + equals = { x, y -> x == y }, + fetch = { arr[it] }, + store = { i, x -> arr[i] = x } + ) + +// FILE: inline.kt +inline fun put( + x: T, + maxExclusive: Int, + isEmpty: (Int) -> Boolean, + equals: (T, T) -> Boolean, + fetch: (Int) -> T, + store: (Int, T) -> Unit +): Boolean { + var i = 0 + do { + if (isEmpty(i)) { + store(i, x) + return true + } + + val y = fetch(i) + if (equals(x, y)) { + return false + } + + i++ + if (i >= maxExclusive) return false + } while (true) +} + diff --git a/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValues.txt b/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValues.txt new file mode 100644 index 00000000000..dbe8c15f6c5 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValues.txt @@ -0,0 +1,6 @@ +@kotlin.Metadata +public final class TaintedValuesKt { + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method getOrElse(): int + public final static method isNotEmpty(@org.jetbrains.annotations.NotNull p0: java.util.ArrayList): boolean +} diff --git a/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValuesBox.txt b/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValuesBox.txt new file mode 100644 index 00000000000..72fa94ae473 --- /dev/null +++ b/compiler/testData/codegen/light-analysis/boxingOptimization/taintedValuesBox.txt @@ -0,0 +1,9 @@ +@kotlin.Metadata +public final class TaintedValuesBoxKt { + public final static field SIZE: int + private final static @org.jetbrains.annotations.NotNull field arr: int[] + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static @org.jetbrains.annotations.NotNull method getArr(): int[] + public final static method put(p0: java.lang.Object, p1: int, @org.jetbrains.annotations.NotNull p2: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.NotNull p3: kotlin.jvm.functions.Function2, @org.jetbrains.annotations.NotNull p4: kotlin.jvm.functions.Function1, @org.jetbrains.annotations.NotNull p5: kotlin.jvm.functions.Function2): boolean + public final static method putNonNegInt(p0: int): boolean +} diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 94657307a3d..1a17cbcc8bf 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -938,6 +938,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("taintedValues.kt") + public void testTaintedValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt"); + doTest(fileName); + } + + @TestMetadata("taintedValuesBox.kt") + public void testTaintedValuesBox() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt"); + doTest(fileName); + } + @TestMetadata("unsafeRemoving.kt") public void testUnsafeRemoving() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 54552401f3e..bf1907e8ba6 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -938,6 +938,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("taintedValues.kt") + public void testTaintedValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt"); + doTest(fileName); + } + + @TestMetadata("taintedValuesBox.kt") + public void testTaintedValuesBox() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt"); + doTest(fileName); + } + @TestMetadata("unsafeRemoving.kt") public void testUnsafeRemoving() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 978d4d758b7..8c6cf73c774 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -473,6 +473,18 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { doTest(fileName); } + @TestMetadata("kt15862.kt") + public void testKt15862() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862.kt"); + doTest(fileName); + } + + @TestMetadata("kt15862_2.kt") + public void testKt15862_2() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt15862_2.kt"); + doTest(fileName); + } + @TestMetadata("kt6842.kt") public void testKt6842() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/boxingOptimization/kt6842.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java index 6a1c7b5324f..94c880ff15b 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeCodegenTestGenerated.java @@ -938,6 +938,18 @@ public class LightAnalysisModeCodegenTestGenerated extends AbstractLightAnalysis doTest(fileName); } + @TestMetadata("taintedValues.kt") + public void testTaintedValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt"); + doTest(fileName); + } + + @TestMetadata("taintedValuesBox.kt") + public void testTaintedValuesBox() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt"); + doTest(fileName); + } + @TestMetadata("unsafeRemoving.kt") public void testUnsafeRemoving() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 088d01d6c14..b3c5104352d 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -1160,6 +1160,18 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("taintedValues.kt") + public void testTaintedValues() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValues.kt"); + doTest(fileName); + } + + @TestMetadata("taintedValuesBox.kt") + public void testTaintedValuesBox() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/taintedValuesBox.kt"); + doTest(fileName); + } + @TestMetadata("unsafeRemoving.kt") public void testUnsafeRemoving() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/boxingOptimization/unsafeRemoving.kt");