KT-15862 Inline generic functions can unexpectedly box primitives

Previous version of the boxing/unboxing analysis treated merging boxed and non-boxed values as a hazard.
If such merged values are not used (e.g., early return + local variables reused in inlined calls),
corresponding boxing/unboxing operations still can be optimized out.

All information related to boxed value usage by instructions is moved to 'BoxedValueDescriptor'.
Introduce "tainted" (and "clean") boxed values, with the following rules:

  merge(B, B) = B, if unboxed types are compatible,
                T, otherwise

  merge(B, X) = T

  merge(T, X) = T

  where
    X is a non-boxed value,
    B is a "clean" boxed value,
    T is a "tainted" boxed value.

Postpone decision about value merge hazards until a "tainted" value is used.
This commit is contained in:
Dmitry Petrov
2017-02-03 19:49:56 +03:00
parent 98269c10d8
commit e0cea468fa
16 changed files with 406 additions and 144 deletions
@@ -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<AbstractInsnNode>()
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
private val associatedVariables = HashSet<Int>()
private val mergedWith = HashSet<BoxedBasicValue>()
private val mergedWith = HashSet<BoxedValueDescriptor>()
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<AbstractInsnNode> =
ArrayList(associatedInsns)
fun getAssociatedInsns() = associatedInsns.toReadOnlyList()
fun addInsn(insnNode: AbstractInsnNode) {
associatedInsns.add(insnNode)
@@ -61,22 +79,20 @@ class BoxedBasicValue(
fun getVariablesIndexes(): List<Int> =
ArrayList(associatedVariables)
fun addMergedWith(value: BoxedBasicValue) {
mergedWith.add(value)
fun addMergedWith(descriptor: BoxedValueDescriptor) {
mergedWith.add(descriptor)
}
fun getMergedWith(): Iterable<BoxedBasicValue> =
fun getMergedWith(): Iterable<BoxedValueDescriptor> =
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<Pair<AbstractInsnNode, Type>> =
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")
}
@@ -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>): 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")
@@ -22,25 +22,25 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class RedundantBoxedValuesCollection implements Iterable<BoxedBasicValue> {
private final Set<BoxedBasicValue> safeToDeleteValues = new HashSet<BoxedBasicValue>();
public class RedundantBoxedValuesCollection implements Iterable<BoxedValueDescriptor> {
private final Set<BoxedValueDescriptor> safeToDeleteValues = new HashSet<BoxedValueDescriptor>();
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<BoxedBasicValue>
@NotNull
@Override
public Iterator<BoxedBasicValue> iterator() {
public Iterator<BoxedValueDescriptor> iterator() {
return safeToDeleteValues.iterator();
}
}
@@ -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)
}
}
}
@@ -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<BasicValue>[] frames = analyze(
internalClassName, node, interpreter
);
Frame<BasicValue>[] frames = analyze(internalClassName, node, interpreter);
interpretPopInstructionsForBoxedValues(interpreter, node, frames);
RedundantBoxedValuesCollection valuesToOptimize = interpreter.getCandidatesBoxedValues();
@@ -99,32 +96,25 @@ public class RedundantBoxingMethodTransformer extends MethodTransformer {
continue;
}
List<BasicValue> usedValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames);
List<BasicValue> variableValues = getValuesStoredOrLoadedToVariable(localVariableNode, node, frames);
Collection<BasicValue> boxed = Collections2.filter(usedValues, new Predicate<BasicValue>() {
Collection<BasicValue> boxed = CollectionsKt.filter(variableValues, new Function1<BasicValue, Boolean>() {
@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<BasicValue, Boolean>() {
@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<BasicValue> usedValues, final Type unboxedType) {
return CollectionsKt.any(usedValues, new Function1<BasicValue, Boolean>() {
@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<BasicValue>[] 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<Integer> doubleSizedVars = new HashSet<Integer>();
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<AbstractInsnNode, Type> 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<AbstractInsnNode, Type> 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
)
);
@@ -0,0 +1,11 @@
// WITH_RUNTIME
// Just make sure there's no VerifyError
fun getOrElse() =
mapOf<String, Int>().getOrElse("foo") { 3 }
fun isNotEmpty(l: ArrayList<Int>) =
l.iterator()?.hasNext() ?: false
fun box() = "OK"
@@ -0,0 +1,49 @@
// WITH_RUNTIME
inline fun <T> 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"
}
@@ -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 <T> 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)
}
@@ -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 <T> 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)
}
@@ -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
}
@@ -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
}
@@ -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");
@@ -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");
@@ -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");
@@ -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");
@@ -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");