Perform bytecode optimisations for inline classes
#KT-23742 Fixed
This commit is contained in:
+19
-19
@@ -39,6 +39,25 @@ class OptimizationMethodVisitor(
|
|||||||
private val constructorCallNormalizationTransformer =
|
private val constructorCallNormalizationTransformer =
|
||||||
UninitializedStoresMethodTransformer(generationState.constructorCallNormalizationMode)
|
UninitializedStoresMethodTransformer(generationState.constructorCallNormalizationMode)
|
||||||
|
|
||||||
|
val normalizationMethodTransformer = CompositeMethodTransformer(
|
||||||
|
FixStackWithLabelNormalizationMethodTransformer(),
|
||||||
|
MethodVerifier("AFTER mandatory stack transformations")
|
||||||
|
)
|
||||||
|
|
||||||
|
val optimizationTransformer = CompositeMethodTransformer(
|
||||||
|
CapturedVarsOptimizationMethodTransformer(),
|
||||||
|
RedundantNullCheckMethodTransformer(generationState),
|
||||||
|
RedundantCheckCastEliminationMethodTransformer(),
|
||||||
|
ConstantConditionEliminationMethodTransformer(),
|
||||||
|
RedundantBoxingMethodTransformer(generationState),
|
||||||
|
StackPeepholeOptimizationsTransformer(),
|
||||||
|
PopBackwardPropagationTransformer(),
|
||||||
|
DeadCodeEliminationMethodTransformer(),
|
||||||
|
RedundantGotoMethodTransformer(),
|
||||||
|
RedundantNopsCleanupMethodTransformer(),
|
||||||
|
MethodVerifier("AFTER optimizations")
|
||||||
|
)
|
||||||
|
|
||||||
override fun performTransformations(methodNode: MethodNode) {
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
normalizationMethodTransformer.transform("fake", methodNode)
|
normalizationMethodTransformer.transform("fake", methodNode)
|
||||||
constructorCallNormalizationTransformer.transform("fake", methodNode)
|
constructorCallNormalizationTransformer.transform("fake", methodNode)
|
||||||
@@ -53,25 +72,6 @@ class OptimizationMethodVisitor(
|
|||||||
companion object {
|
companion object {
|
||||||
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
private val MEMORY_LIMIT_BY_METHOD_MB = 50
|
||||||
|
|
||||||
val normalizationMethodTransformer = CompositeMethodTransformer(
|
|
||||||
FixStackWithLabelNormalizationMethodTransformer(),
|
|
||||||
MethodVerifier("AFTER mandatory stack transformations")
|
|
||||||
)
|
|
||||||
|
|
||||||
val optimizationTransformer = CompositeMethodTransformer(
|
|
||||||
CapturedVarsOptimizationMethodTransformer(),
|
|
||||||
RedundantNullCheckMethodTransformer(),
|
|
||||||
RedundantCheckCastEliminationMethodTransformer(),
|
|
||||||
ConstantConditionEliminationMethodTransformer(),
|
|
||||||
RedundantBoxingMethodTransformer(),
|
|
||||||
StackPeepholeOptimizationsTransformer(),
|
|
||||||
PopBackwardPropagationTransformer(),
|
|
||||||
DeadCodeEliminationMethodTransformer(),
|
|
||||||
RedundantGotoMethodTransformer(),
|
|
||||||
RedundantNopsCleanupMethodTransformer(),
|
|
||||||
MethodVerifier("AFTER optimizations")
|
|
||||||
)
|
|
||||||
|
|
||||||
fun canBeOptimized(node: MethodNode): Boolean {
|
fun canBeOptimized(node: MethodNode): Boolean {
|
||||||
val totalFramesSizeMb = node.instructions.size() * (node.maxLocals + node.maxStack) / (1024 * 1024)
|
val totalFramesSizeMb = node.instructions.size() * (node.maxLocals + node.maxStack) / (1024 * 1024)
|
||||||
return totalFramesSizeMb < MEMORY_LIMIT_BY_METHOD_MB
|
return totalFramesSizeMb < MEMORY_LIMIT_BY_METHOD_MB
|
||||||
|
|||||||
+16
-6
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.boxing
|
|||||||
import com.intellij.openapi.util.Pair
|
import com.intellij.openapi.util.Pair
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
@@ -36,9 +37,10 @@ abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
|
|||||||
class CleanBoxedValue(
|
class CleanBoxedValue(
|
||||||
boxedType: Type,
|
boxedType: Type,
|
||||||
boxingInsn: AbstractInsnNode,
|
boxingInsn: AbstractInsnNode,
|
||||||
progressionIterator: ProgressionIteratorBasicValue?
|
progressionIterator: ProgressionIteratorBasicValue?,
|
||||||
|
val generationState: GenerationState
|
||||||
) : BoxedBasicValue(boxedType) {
|
) : BoxedBasicValue(boxedType) {
|
||||||
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator)
|
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator, generationState)
|
||||||
|
|
||||||
private var tainted: TaintedBoxedValue? = null
|
private var tainted: TaintedBoxedValue? = null
|
||||||
override fun taint(): BoxedBasicValue = tainted ?: TaintedBoxedValue(this).also { tainted = it }
|
override fun taint(): BoxedBasicValue = tainted ?: TaintedBoxedValue(this).also { tainted = it }
|
||||||
@@ -55,7 +57,8 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas
|
|||||||
class BoxedValueDescriptor(
|
class BoxedValueDescriptor(
|
||||||
private val boxedType: Type,
|
private val boxedType: Type,
|
||||||
val boxingInsn: AbstractInsnNode,
|
val boxingInsn: AbstractInsnNode,
|
||||||
val progressionIterator: ProgressionIteratorBasicValue?
|
val progressionIterator: ProgressionIteratorBasicValue?,
|
||||||
|
val generationState: GenerationState
|
||||||
) {
|
) {
|
||||||
private val associatedInsns = HashSet<AbstractInsnNode>()
|
private val associatedInsns = HashSet<AbstractInsnNode>()
|
||||||
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
||||||
@@ -63,7 +66,7 @@ class BoxedValueDescriptor(
|
|||||||
private val mergedWith = HashSet<BoxedValueDescriptor>()
|
private val mergedWith = HashSet<BoxedValueDescriptor>()
|
||||||
|
|
||||||
var isSafeToRemove = true; private set
|
var isSafeToRemove = true; private set
|
||||||
val unboxedType: Type = getUnboxedType(boxedType)
|
val unboxedType: Type = getUnboxedType(boxedType, generationState)
|
||||||
|
|
||||||
fun getAssociatedInsns() = associatedInsns.toList()
|
fun getAssociatedInsns() = associatedInsns.toList()
|
||||||
|
|
||||||
@@ -102,11 +105,18 @@ class BoxedValueDescriptor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun getUnboxedType(boxedType: Type): Type {
|
fun getUnboxedType(boxedType: Type, state: GenerationState): Type {
|
||||||
val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType)
|
val primitiveType = AsmUtil.unboxPrimitiveTypeOrNull(boxedType)
|
||||||
if (primitiveType != null) return primitiveType
|
if (primitiveType != null) return primitiveType
|
||||||
|
|
||||||
if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE
|
if (boxedType == AsmTypes.K_CLASS_TYPE) return AsmTypes.JAVA_CLASS_TYPE
|
||||||
|
|
||||||
throw IllegalArgumentException("Expected primitive type wrapper or KClass, got: $boxedType")
|
unboxedTypeOfInlineClass(boxedType, state)?.let { return it }
|
||||||
|
|
||||||
|
throw IllegalArgumentException("Expected primitive type wrapper or KClass or inline class wrapper, got: $boxedType")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun unboxedTypeOfInlineClass(boxedType: Type, state: GenerationState): Type? {
|
||||||
|
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(boxedType).singleOrNull() ?: return null
|
||||||
|
return state.typeMapper.mapType(descriptor.defaultType)
|
||||||
}
|
}
|
||||||
|
|||||||
+50
-11
@@ -22,7 +22,10 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
|||||||
import org.jetbrains.kotlin.codegen.isRangeOrProgression
|
import org.jetbrains.kotlin.codegen.isRangeOrProgression
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
|
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.name.FqName
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import org.jetbrains.kotlin.resolve.InlineClassDescriptorResolver
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
@@ -33,7 +36,10 @@ import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
|
open class BoxingInterpreter(
|
||||||
|
private val insnList: InsnList,
|
||||||
|
private val generationState: GenerationState
|
||||||
|
) : OptimizationBasicInterpreter() {
|
||||||
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
|
||||||
|
|
||||||
protected open fun createNewBoxing(
|
protected open fun createNewBoxing(
|
||||||
@@ -42,7 +48,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
progressionIterator: ProgressionIteratorBasicValue?
|
progressionIterator: ProgressionIteratorBasicValue?
|
||||||
): BasicValue =
|
): BasicValue =
|
||||||
boxingPlaces.getOrPut(insnList.indexOf(insn)) {
|
boxingPlaces.getOrPut(insnList.indexOf(insn)) {
|
||||||
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
|
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator, generationState)
|
||||||
onNewBoxedValue(boxedBasicValue)
|
onNewBoxedValue(boxedBasicValue)
|
||||||
boxedBasicValue
|
boxedBasicValue
|
||||||
}
|
}
|
||||||
@@ -62,10 +68,10 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
val firstArg = values.firstOrNull() ?: return value
|
val firstArg = values.firstOrNull() ?: return value
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
insn.isBoxing() -> {
|
insn.isBoxing(generationState) -> {
|
||||||
createNewBoxing(insn, value.type, null)
|
createNewBoxing(insn, value.type, null)
|
||||||
}
|
}
|
||||||
insn.isUnboxing() && firstArg is BoxedBasicValue -> {
|
insn.isUnboxing(generationState) && firstArg is BoxedBasicValue -> {
|
||||||
onUnboxing(insn, firstArg, value.type)
|
onUnboxing(insn, firstArg, value.type)
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
@@ -76,7 +82,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
|
|||||||
?: throw AssertionError("firstArg should be progression iterator")
|
?: throw AssertionError("firstArg should be progression iterator")
|
||||||
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
|
||||||
}
|
}
|
||||||
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> {
|
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values, generationState) -> {
|
||||||
onAreEqual(insn, values[0] as BoxedBasicValue, values[1] as BoxedBasicValue)
|
onAreEqual(insn, values[0] as BoxedBasicValue, values[1] as BoxedBasicValue)
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
@@ -149,11 +155,11 @@ private val UNBOXING_METHOD_NAMES =
|
|||||||
private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE)
|
private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE)
|
||||||
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
||||||
|
|
||||||
fun AbstractInsnNode.isUnboxing() =
|
fun AbstractInsnNode.isUnboxing(state: GenerationState) =
|
||||||
isPrimitiveUnboxing() || isJavaLangClassUnboxing()
|
isPrimitiveUnboxing() || isJavaLangClassUnboxing() || isInlineClassUnboxing(state)
|
||||||
|
|
||||||
fun AbstractInsnNode.isBoxing() =
|
fun AbstractInsnNode.isBoxing(state: GenerationState) =
|
||||||
isPrimitiveBoxing() || isJavaLangClassBoxing()
|
isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state)
|
||||||
|
|
||||||
fun AbstractInsnNode.isPrimitiveUnboxing() =
|
fun AbstractInsnNode.isPrimitiveUnboxing() =
|
||||||
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
||||||
@@ -202,6 +208,39 @@ fun AbstractInsnNode.isJavaLangClassBoxing() =
|
|||||||
desc == JLCLASS_TO_KCLASS
|
desc == JLCLASS_TO_KCLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun AbstractInsnNode.isInlineClassBoxing(state: GenerationState) =
|
||||||
|
isMethodInsnWith(Opcodes.INVOKESTATIC) {
|
||||||
|
isInlineClassBoxingMethodDescriptor(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AbstractInsnNode.isInlineClassUnboxing(state: GenerationState) =
|
||||||
|
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
|
||||||
|
isInlineClassUnboxingMethodDescriptor(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MethodInsnNode.isInlineClassBoxingMethodDescriptor(state: GenerationState): Boolean {
|
||||||
|
if (name != InlineClassDescriptorResolver.BOX_METHOD_NAME.asString()) return false
|
||||||
|
if (!owner.endsWith(JvmAbi.ERASED_INLINE_CLASS_SUFFIX)) return false
|
||||||
|
|
||||||
|
val ownerType = Type.getObjectType(owner.removeSuffix(JvmAbi.ERASED_INLINE_CLASS_SUFFIX))
|
||||||
|
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(ownerType).singleOrNull() ?: return false
|
||||||
|
|
||||||
|
if (!descriptor.isInline) return false
|
||||||
|
|
||||||
|
return desc == Type.getMethodDescriptor(ownerType, state.typeMapper.mapType(descriptor.defaultType))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun MethodInsnNode.isInlineClassUnboxingMethodDescriptor(state: GenerationState): Boolean {
|
||||||
|
if (name != InlineClassDescriptorResolver.UNBOX_METHOD_NAME.asString()) return false
|
||||||
|
|
||||||
|
val ownerType = Type.getObjectType(owner)
|
||||||
|
val descriptor = state.jvmBackendClassResolver.resolveToClassDescriptors(ownerType).singleOrNull() ?: return false
|
||||||
|
|
||||||
|
if (!descriptor.isInline) return false
|
||||||
|
|
||||||
|
return desc == Type.getMethodDescriptor(state.typeMapper.mapType(descriptor.defaultType))
|
||||||
|
}
|
||||||
|
|
||||||
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
|
||||||
values.firstOrNull() is ProgressionIteratorBasicValue &&
|
values.firstOrNull() is ProgressionIteratorBasicValue &&
|
||||||
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
|
||||||
@@ -239,8 +278,8 @@ fun AbstractInsnNode.isAreEqualIntrinsic() =
|
|||||||
|
|
||||||
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
|
||||||
|
|
||||||
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
|
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>, generationState: GenerationState): Boolean =
|
||||||
values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers }
|
values.none { getUnboxedType(it.type, generationState) in shouldUseEqualsForWrappers }
|
||||||
|
|
||||||
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||||
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
||||||
|
|||||||
+5
-1
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization.boxing
|
package org.jetbrains.kotlin.codegen.optimization.boxing
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableSet
|
import com.google.common.collect.ImmutableSet
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
@@ -25,7 +26,10 @@ import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.VarInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
|
|
||||||
internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) {
|
internal class RedundantBoxingInterpreter(
|
||||||
|
insnList: InsnList,
|
||||||
|
generationState: GenerationState
|
||||||
|
) : BoxingInterpreter(insnList, generationState) {
|
||||||
|
|
||||||
val candidatesBoxedValues = RedundantBoxedValuesCollection()
|
val candidatesBoxedValues = RedundantBoxedValuesCollection()
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -25,6 +25,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.remapLocalVariables
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
|
||||||
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -34,10 +35,10 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class RedundantBoxingMethodTransformer : MethodTransformer() {
|
class RedundantBoxingMethodTransformer(private val generationState: GenerationState) : MethodTransformer() {
|
||||||
|
|
||||||
override fun transform(internalClassName: String, node: MethodNode) {
|
override fun transform(internalClassName: String, node: MethodNode) {
|
||||||
val interpreter = RedundantBoxingInterpreter(node.instructions)
|
val interpreter = RedundantBoxingInterpreter(node.instructions, generationState)
|
||||||
val frames = MethodTransformer.analyze(internalClassName, node, interpreter)
|
val frames = MethodTransformer.analyze(internalClassName, node, interpreter)
|
||||||
|
|
||||||
interpretPopInstructionsForBoxedValues(interpreter, node, frames)
|
interpretPopInstructionsForBoxedValues(interpreter, node, frames)
|
||||||
|
|||||||
+3
-2
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpr
|
|||||||
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||||
@@ -30,7 +31,7 @@ import org.jetbrains.org.objectweb.asm.tree.MethodInsnNode
|
|||||||
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
|
||||||
|
|
||||||
class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
class NullabilityInterpreter(private val generationState: GenerationState) : OptimizationBasicInterpreter() {
|
||||||
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
override fun newOperation(insn: AbstractInsnNode): BasicValue? {
|
||||||
val defaultResult = super.newOperation(insn)
|
val defaultResult = super.newOperation(insn)
|
||||||
val resultType = defaultResult?.type
|
val resultType = defaultResult?.type
|
||||||
@@ -80,7 +81,7 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
|
|||||||
val resultType = defaultResult?.type
|
val resultType = defaultResult?.type
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
insn.isBoxing() ->
|
insn.isBoxing(generationState) ->
|
||||||
NotNullBasicValue(resultType)
|
NotNullBasicValue(resultType)
|
||||||
insn.isIteratorMethodCallOfProgression(values) ->
|
insn.isIteratorMethodCallOfProgression(values) ->
|
||||||
ProgressionIteratorBasicValue.byProgressionClassType(values[0].type)
|
ProgressionIteratorBasicValue.byProgressionClassType(values[0].type)
|
||||||
|
|||||||
+5
-4
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
|
|||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.asNotNull
|
import org.jetbrains.kotlin.codegen.pseudoInsns.asNotNull
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
import org.jetbrains.kotlin.codegen.pseudoInsns.isPseudo
|
||||||
|
import org.jetbrains.kotlin.codegen.state.GenerationState
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
@@ -35,13 +36,13 @@ import org.jetbrains.org.objectweb.asm.Opcodes
|
|||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
import org.jetbrains.org.objectweb.asm.tree.*
|
import org.jetbrains.org.objectweb.asm.tree.*
|
||||||
|
|
||||||
class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
class RedundantNullCheckMethodTransformer(private val generationState: GenerationState) : MethodTransformer() {
|
||||||
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
override fun transform(internalClassName: String, methodNode: MethodNode) {
|
||||||
while (TransformerPass(internalClassName, methodNode).run()) {
|
while (TransformerPass(internalClassName, methodNode, generationState).run()) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) {
|
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode, val generationState: GenerationState) {
|
||||||
private var changes = false
|
private var changes = false
|
||||||
|
|
||||||
fun run(): Boolean {
|
fun run(): Boolean {
|
||||||
@@ -59,7 +60,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> {
|
private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> {
|
||||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
|
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter(generationState))
|
||||||
val insns = methodNode.instructions.toArray()
|
val insns = methodNode.instructions.toArray()
|
||||||
val nullabilityMap = LinkedHashMap<AbstractInsnNode, StrictBasicValue>()
|
val nullabilityMap = LinkedHashMap<AbstractInsnNode, StrictBasicValue>()
|
||||||
for (i in insns.indices) {
|
for (i in insns.indices) {
|
||||||
|
|||||||
+15
-1
@@ -1,3 +1,5 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
|
||||||
// https://youtrack.jetbrains.com/issue/KT-15871
|
// https://youtrack.jetbrains.com/issue/KT-15871
|
||||||
|
|
||||||
// FILE: Test.kt
|
// FILE: Test.kt
|
||||||
@@ -10,8 +12,20 @@ fun getAndCheckInt(a: Int, b: Int) =
|
|||||||
// 0 Value
|
// 0 Value
|
||||||
// 0 areEqual
|
// 0 areEqual
|
||||||
|
|
||||||
|
// FILE: TestInlined.kt
|
||||||
|
|
||||||
|
fun getAndCheckInlinedInt(a: InlinedInt, b: InlinedInt) =
|
||||||
|
getAndCheck({ a }, { b })
|
||||||
|
|
||||||
|
// @TestInlinedKt.class:
|
||||||
|
// 0 valueOf
|
||||||
|
// 0 Value
|
||||||
|
// 0 areEqual
|
||||||
|
// 0 INVOKESTATIC InlinedInt\$Erased.box
|
||||||
|
// 0 INVOKEVIRTUAL InlinedInt.unbox
|
||||||
|
|
||||||
// FILE: Inline.kt
|
// FILE: Inline.kt
|
||||||
inline fun <T> getAndCheck(getFirst: () -> T, getSecond: () -> T) =
|
inline fun <T> getAndCheck(getFirst: () -> T, getSecond: () -> T) =
|
||||||
getFirst() == getSecond()
|
getFirst() == getSecond()
|
||||||
|
|
||||||
|
inline class InlinedInt(val x: Int)
|
||||||
|
|||||||
+22
@@ -1,3 +1,6 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
|
||||||
|
// FILE: Test.kt
|
||||||
|
|
||||||
inline fun <R, T> foo(x : R, y : R, block : (R) -> T) : T {
|
inline fun <R, T> foo(x : R, y : R, block : (R) -> T) : T {
|
||||||
val a = x is Number
|
val a = x is Number
|
||||||
@@ -17,7 +20,26 @@ fun bar() {
|
|||||||
foo(1, 2) { x -> x is Int }
|
foo(1, 2) { x -> x is Int }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
// 0 Value\s\(\)
|
// 0 Value\s\(\)
|
||||||
// 2 INSTANCEOF
|
// 2 INSTANCEOF
|
||||||
// 1 CHECKCAST
|
// 1 CHECKCAST
|
||||||
|
|
||||||
|
// FILE: Inline.kt
|
||||||
|
|
||||||
|
inline class InlinedInt(val x: Int)
|
||||||
|
|
||||||
|
// FILE: TestInlined.kt
|
||||||
|
|
||||||
|
fun baz() {
|
||||||
|
foo(InlinedInt(1), InlinedInt(2)) { x -> x is InlinedInt }
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TestInlinedKt.class:
|
||||||
|
// 0 valueOf
|
||||||
|
// 0 Value\s\(\)
|
||||||
|
// 0 INSTANCEOF
|
||||||
|
// 0 CHECKCAST
|
||||||
|
// 0 INVOKESTATIC InlinedInt\$Erased.box
|
||||||
|
// 0 INVOKEVIRTUAL InlinedInt.unbox
|
||||||
|
|||||||
+36
@@ -0,0 +1,36 @@
|
|||||||
|
// !LANGUAGE: +InlineClasses
|
||||||
|
|
||||||
|
|
||||||
|
// FILE: dependency.kt
|
||||||
|
|
||||||
|
inline class InlinedInt(val internal: Int)
|
||||||
|
inline class InlinedString(val internal: String)
|
||||||
|
|
||||||
|
inline fun <T> foo(callback: () -> T): T {
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun bar(callback: () -> InlinedInt): InlinedInt {
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun baz(callback: () -> InlinedString): InlinedString {
|
||||||
|
return callback()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun test(i: InlinedInt, s: InlinedString) {
|
||||||
|
foo { i }
|
||||||
|
bar { i }
|
||||||
|
|
||||||
|
foo { s }
|
||||||
|
baz { s }
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TestKt.class:
|
||||||
|
// 0 valueOf
|
||||||
|
// 0 INVOKESTATIC InlinedInt\$Erased.box
|
||||||
|
// 0 INVOKEVIRTUAL InlinedInt.unbox
|
||||||
|
// 0 INVOKESTATIC InlinedString\$Erased.box
|
||||||
|
// 0 INVOKEVIRTUAL InlinedString.unbox
|
||||||
Vendored
+1
-1
@@ -6,7 +6,7 @@ fun f() {
|
|||||||
val unull = UInt(1) ?: null
|
val unull = UInt(1) ?: null
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 INVOKESTATIC UInt\$Erased.box
|
// 0 INVOKESTATIC UInt\$Erased.box
|
||||||
// 0 INVOKEVIRTUAL UInt.unbox
|
// 0 INVOKEVIRTUAL UInt.unbox
|
||||||
|
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
|
|||||||
Vendored
+2
-2
@@ -12,8 +12,8 @@ fun test(x: UInt?, y: UInt) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 INVOKESTATIC UInt\$Erased.box
|
// 0 INVOKESTATIC UInt\$Erased.box
|
||||||
// 3 INVOKEVIRTUAL UInt.unbox
|
// 1 INVOKEVIRTUAL UInt.unbox
|
||||||
|
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
// 0 intValue
|
// 0 intValue
|
||||||
+5
-5
@@ -9,17 +9,17 @@ fun <T> T.idExtension(): T = this
|
|||||||
inline fun <T> T.inlinedIdExtension(): T = this
|
inline fun <T> T.inlinedIdExtension(): T = this
|
||||||
|
|
||||||
fun test(f: Foo) {
|
fun test(f: Foo) {
|
||||||
inlinedId(f) // box
|
inlinedId(f)
|
||||||
inlinedId(f).idExtension() // box
|
inlinedId(f).idExtension() // box
|
||||||
|
|
||||||
f.inlinedIdExtension() // box
|
f.inlinedIdExtension()
|
||||||
|
|
||||||
val a = inlinedId(f).idExtension() // box unbox
|
val a = inlinedId(f).idExtension() // box unbox
|
||||||
val b = inlinedId(f).inlinedIdExtension() // box unbox
|
val b = inlinedId(f).inlinedIdExtension()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5 INVOKESTATIC Foo\$Erased.box
|
// 2 INVOKESTATIC Foo\$Erased.box
|
||||||
// 2 INVOKEVIRTUAL Foo.unbox
|
// 1 INVOKEVIRTUAL Foo.unbox
|
||||||
|
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
// 0 intValue
|
// 0 intValue
|
||||||
+15
-15
@@ -7,39 +7,39 @@ inline class InlineNullableReference(val a: Any?)
|
|||||||
|
|
||||||
fun test1(a: InlineNotNullPrimitive) {
|
fun test1(a: InlineNotNullPrimitive) {
|
||||||
val a0 = a
|
val a0 = a
|
||||||
val a1: Any = a // box
|
val a1: Any = a
|
||||||
val a2: Any? = a // box
|
val a2: Any? = a
|
||||||
val a3: InlineNotNullPrimitive = a
|
val a3: InlineNotNullPrimitive = a
|
||||||
val a4: InlineNotNullPrimitive? = a // box
|
val a4: InlineNotNullPrimitive? = a
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test2(b: InlineNullablePrimitive) {
|
fun test2(b: InlineNullablePrimitive) {
|
||||||
val b0 = b
|
val b0 = b
|
||||||
val b1: Any = b // box
|
val b1: Any = b
|
||||||
val b2: Any? = b // box
|
val b2: Any? = b
|
||||||
val b3: InlineNullablePrimitive = b
|
val b3: InlineNullablePrimitive = b
|
||||||
val b4: InlineNullablePrimitive? = b // box
|
val b4: InlineNullablePrimitive? = b
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test3(c: InlineNotNullReference) {
|
fun test3(c: InlineNotNullReference) {
|
||||||
val c0 = c
|
val c0 = c
|
||||||
val c1: Any = c // box
|
val c1: Any = c
|
||||||
val c2: Any? = c // box
|
val c2: Any? = c
|
||||||
val c3: InlineNotNullReference = c
|
val c3: InlineNotNullReference = c
|
||||||
val c4: InlineNotNullReference? = c
|
val c4: InlineNotNullReference? = c
|
||||||
}
|
}
|
||||||
|
|
||||||
fun test4(d: InlineNullableReference) {
|
fun test4(d: InlineNullableReference) {
|
||||||
val d0 = d
|
val d0 = d
|
||||||
val d1: Any = d // box
|
val d1: Any = d
|
||||||
val d2: Any? = d // box
|
val d2: Any? = d
|
||||||
val d3: InlineNullableReference = d
|
val d3: InlineNullableReference = d
|
||||||
val d4: InlineNullableReference? = d // box
|
val d4: InlineNullableReference? = d
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 INVOKESTATIC InlineNotNullPrimitive\$Erased.box
|
// 0 INVOKESTATIC InlineNotNullPrimitive\$Erased.box
|
||||||
// 3 INVOKESTATIC InlineNullablePrimitive\$Erased.box
|
// 0 INVOKESTATIC InlineNullablePrimitive\$Erased.box
|
||||||
// 2 INVOKESTATIC InlineNotNullReference\$Erased.box
|
// 0 INVOKESTATIC InlineNotNullReference\$Erased.box
|
||||||
// 3 INVOKESTATIC InlineNullableReference\$Erased.box
|
// 0 INVOKESTATIC InlineNullableReference\$Erased.box
|
||||||
|
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
+2
-2
@@ -16,8 +16,8 @@ fun UIntArray.swap(i: Int, j: Int) {
|
|||||||
this[j] = this[i].also { this[i] = this[j] }
|
this[j] = this[i].also { this[i] = this[j] }
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2 INVOKEVIRTUAL UInt.unbox
|
// 0 INVOKEVIRTUAL UInt.unbox
|
||||||
// 1 INVOKESTATIC UInt\$Erased.box
|
// 0 INVOKESTATIC UInt\$Erased.box
|
||||||
|
|
||||||
// 0 intValue
|
// 0 intValue
|
||||||
// 0 valueOf
|
// 0 valueOf
|
||||||
@@ -424,6 +424,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/fold.kt");
|
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/fold.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("inlineClassesAndInlinedLambda.kt")
|
||||||
|
public void testInlineClassesAndInlinedLambda() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/inlineClassesAndInlinedLambda.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("intCompareTo.kt")
|
@TestMetadata("intCompareTo.kt")
|
||||||
public void testIntCompareTo() throws Exception {
|
public void testIntCompareTo() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/intCompareTo.kt");
|
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/intCompareTo.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user