From 01d9be65bc7a8156e9746dd182c04e09db1ef8da Mon Sep 17 00:00:00 2001 From: Mikhail Zarechenskiy Date: Fri, 15 Jun 2018 15:36:47 +0300 Subject: [PATCH] Perform bytecode optimisations for inline classes #KT-23742 Fixed --- .../optimization/OptimizationMethodVisitor.kt | 38 ++++++------ .../optimization/boxing/BoxedBasicValue.kt | 22 +++++-- .../optimization/boxing/BoxingInterpreter.kt | 61 +++++++++++++++---- .../boxing/RedundantBoxingInterpreter.kt | 6 +- .../RedundantBoxingMethodTransformer.kt | 5 +- .../nullCheck/NullabilityInterpreter.kt | 5 +- .../RedundantNullCheckMethodTransformer.kt | 9 +-- .../boxingOptimization/boxingAndEquals.kt | 16 ++++- .../checkcastAndInstanceOf.kt | 22 +++++++ .../inlineClassesAndInlinedLambda.kt | 36 +++++++++++ ...xInlineClassInsideElvisWithNullConstant.kt | 2 +- .../boxUnboxInsideLambdaAsLastExpression.kt | 4 +- .../boxUnboxOnInlinedParameters.kt | 10 +-- .../inlineClassBoxingOnAssignment.kt | 30 ++++----- .../inlineClasses/uIntArraySwapBoxing.kt | 4 +- .../codegen/BytecodeTextTestGenerated.java | 5 ++ 16 files changed, 204 insertions(+), 71 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/boxingOptimization/inlineClassesAndInlinedLambda.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt index 78c04fd2df2..2524643485b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/OptimizationMethodVisitor.kt @@ -39,6 +39,25 @@ class OptimizationMethodVisitor( private val constructorCallNormalizationTransformer = 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) { normalizationMethodTransformer.transform("fake", methodNode) constructorCallNormalizationTransformer.transform("fake", methodNode) @@ -53,25 +72,6 @@ class OptimizationMethodVisitor( companion object { 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 { val totalFramesSizeMb = node.instructions.size() * (node.maxLocals + node.maxStack) / (1024 * 1024) return totalFramesSizeMb < MEMORY_LIMIT_BY_METHOD_MB 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 ce582dcced2..a596a9546ca 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 @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen.optimization.boxing import com.intellij.openapi.util.Pair import org.jetbrains.kotlin.codegen.AsmUtil 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.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode @@ -36,9 +37,10 @@ abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) { class CleanBoxedValue( boxedType: Type, boxingInsn: AbstractInsnNode, - progressionIterator: ProgressionIteratorBasicValue? + progressionIterator: ProgressionIteratorBasicValue?, + val generationState: GenerationState ) : BoxedBasicValue(boxedType) { - override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator) + override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator, generationState) private var tainted: TaintedBoxedValue? = null override fun taint(): BoxedBasicValue = tainted ?: TaintedBoxedValue(this).also { tainted = it } @@ -55,7 +57,8 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas class BoxedValueDescriptor( private val boxedType: Type, val boxingInsn: AbstractInsnNode, - val progressionIterator: ProgressionIteratorBasicValue? + val progressionIterator: ProgressionIteratorBasicValue?, + val generationState: GenerationState ) { private val associatedInsns = HashSet() private val unboxingWithCastInsns = HashSet>() @@ -63,7 +66,7 @@ class BoxedValueDescriptor( private val mergedWith = HashSet() var isSafeToRemove = true; private set - val unboxedType: Type = getUnboxedType(boxedType) + val unboxedType: Type = getUnboxedType(boxedType, generationState) 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) 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") + 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) } 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 d9f1b2933ef..c5a994fb779 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 @@ -22,7 +22,10 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.isRangeOrProgression import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter 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.resolve.InlineClassDescriptorResolver import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType 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 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() protected open fun createNewBoxing( @@ -42,7 +48,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic progressionIterator: ProgressionIteratorBasicValue? ): BasicValue = boxingPlaces.getOrPut(insnList.indexOf(insn)) { - val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator) + val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator, generationState) onNewBoxedValue(boxedBasicValue) boxedBasicValue } @@ -62,10 +68,10 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic val firstArg = values.firstOrNull() ?: return value return when { - insn.isBoxing() -> { + insn.isBoxing(generationState) -> { createNewBoxing(insn, value.type, null) } - insn.isUnboxing() && firstArg is BoxedBasicValue -> { + insn.isUnboxing(generationState) && firstArg is BoxedBasicValue -> { onUnboxing(insn, firstArg, value.type) value } @@ -76,7 +82,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic ?: throw AssertionError("firstArg should be progression iterator") 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) 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 JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE) -fun AbstractInsnNode.isUnboxing() = - isPrimitiveUnboxing() || isJavaLangClassUnboxing() +fun AbstractInsnNode.isUnboxing(state: GenerationState) = + isPrimitiveUnboxing() || isJavaLangClassUnboxing() || isInlineClassUnboxing(state) -fun AbstractInsnNode.isBoxing() = - isPrimitiveBoxing() || isJavaLangClassBoxing() +fun AbstractInsnNode.isBoxing(state: GenerationState) = + isPrimitiveBoxing() || isJavaLangClassBoxing() || isInlineClassBoxing(state) fun AbstractInsnNode.isPrimitiveUnboxing() = isMethodInsnWith(Opcodes.INVOKEVIRTUAL) { @@ -202,6 +208,39 @@ fun AbstractInsnNode.isJavaLangClassBoxing() = 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) = values.firstOrNull() is ProgressionIteratorBasicValue && isMethodInsnWith(Opcodes.INVOKEINTERFACE) { @@ -239,8 +278,8 @@ fun AbstractInsnNode.isAreEqualIntrinsic() = private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE) -fun canValuesBeUnboxedForAreEqual(values: List): Boolean = - values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers } +fun canValuesBeUnboxedForAreEqual(values: List, generationState: GenerationState): Boolean = + values.none { getUnboxedType(it.type, generationState) in shouldUseEqualsForWrappers } fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List) = isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values) 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 52fb5cdf6ba..41a0361a6bd 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 @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.optimization.boxing 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.Type 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.analysis.BasicValue -internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterpreter(insnList) { +internal class RedundantBoxingInterpreter( + insnList: InsnList, + generationState: GenerationState +) : BoxingInterpreter(insnList, generationState) { val candidatesBoxedValues = RedundantBoxedValuesCollection() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt index 27153bd4f3b..69edbe16b7f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/boxing/RedundantBoxingMethodTransformer.kt @@ -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.top 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.Opcodes 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 java.util.* -class RedundantBoxingMethodTransformer : MethodTransformer() { +class RedundantBoxingMethodTransformer(private val generationState: GenerationState) : MethodTransformer() { 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) interpretPopInstructionsForBoxedValues(interpreter, node, frames) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/NullabilityInterpreter.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/NullabilityInterpreter.kt index 77ff9a38c69..30dd1126629 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/NullabilityInterpreter.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/NullabilityInterpreter.kt @@ -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.pseudoInsns.PseudoInsn 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.Type 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.analysis.BasicValue -class NullabilityInterpreter : OptimizationBasicInterpreter() { +class NullabilityInterpreter(private val generationState: GenerationState) : OptimizationBasicInterpreter() { override fun newOperation(insn: AbstractInsnNode): BasicValue? { val defaultResult = super.newOperation(insn) val resultType = defaultResult?.type @@ -80,7 +81,7 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() { val resultType = defaultResult?.type return when { - insn.isBoxing() -> + insn.isBoxing(generationState) -> NotNullBasicValue(resultType) insn.isIteratorMethodCallOfProgression(values) -> ProgressionIteratorBasicValue.byProgressionClassType(values[0].type) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt index 5c311b8aa69..5ecfe358952 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/nullCheck/RedundantNullCheckMethodTransformer.kt @@ -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.asNotNull 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.utils.SmartList 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.tree.* -class RedundantNullCheckMethodTransformer : MethodTransformer() { +class RedundantNullCheckMethodTransformer(private val generationState: GenerationState) : MethodTransformer() { 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 fun run(): Boolean { @@ -59,7 +60,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() { } private fun analyzeNullabilities(): Map { - val frames = analyze(internalClassName, methodNode, NullabilityInterpreter()) + val frames = analyze(internalClassName, methodNode, NullabilityInterpreter(generationState)) val insns = methodNode.instructions.toArray() val nullabilityMap = LinkedHashMap() for (i in insns.indices) { diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/boxingAndEquals.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/boxingAndEquals.kt index 287598fc8dd..7b0f5e4208f 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/boxingAndEquals.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/boxingAndEquals.kt @@ -1,3 +1,5 @@ +// !LANGUAGE: +InlineClasses + // https://youtrack.jetbrains.com/issue/KT-15871 // FILE: Test.kt @@ -10,8 +12,20 @@ fun getAndCheckInt(a: Int, b: Int) = // 0 Value // 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 inline fun getAndCheck(getFirst: () -> T, getSecond: () -> T) = getFirst() == getSecond() - +inline class InlinedInt(val x: Int) diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/checkcastAndInstanceOf.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/checkcastAndInstanceOf.kt index fa4337128e9..fe7759ab166 100644 --- a/compiler/testData/codegen/bytecodeText/boxingOptimization/checkcastAndInstanceOf.kt +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/checkcastAndInstanceOf.kt @@ -1,3 +1,6 @@ +// !LANGUAGE: +InlineClasses + +// FILE: Test.kt inline fun foo(x : R, y : R, block : (R) -> T) : T { val a = x is Number @@ -17,7 +20,26 @@ fun bar() { foo(1, 2) { x -> x is Int } } +// @TestKt.class: // 0 valueOf // 0 Value\s\(\) // 2 INSTANCEOF // 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 diff --git a/compiler/testData/codegen/bytecodeText/boxingOptimization/inlineClassesAndInlinedLambda.kt b/compiler/testData/codegen/bytecodeText/boxingOptimization/inlineClassesAndInlinedLambda.kt new file mode 100644 index 00000000000..5142c64b089 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/boxingOptimization/inlineClassesAndInlinedLambda.kt @@ -0,0 +1,36 @@ +// !LANGUAGE: +InlineClasses + + +// FILE: dependency.kt + +inline class InlinedInt(val internal: Int) +inline class InlinedString(val internal: String) + +inline fun 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 diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxInlineClassInsideElvisWithNullConstant.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxInlineClassInsideElvisWithNullConstant.kt index 35f6bfef64c..f69719c6c5d 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/boxInlineClassInsideElvisWithNullConstant.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxInlineClassInsideElvisWithNullConstant.kt @@ -6,7 +6,7 @@ fun f() { val unull = UInt(1) ?: null } -// 1 INVOKESTATIC UInt\$Erased.box +// 0 INVOKESTATIC UInt\$Erased.box // 0 INVOKEVIRTUAL UInt.unbox // 0 valueOf diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInsideLambdaAsLastExpression.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInsideLambdaAsLastExpression.kt index 1002fb631b8..ed323f28147 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInsideLambdaAsLastExpression.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxInsideLambdaAsLastExpression.kt @@ -12,8 +12,8 @@ fun test(x: UInt?, y: UInt) { } } -// 2 INVOKESTATIC UInt\$Erased.box -// 3 INVOKEVIRTUAL UInt.unbox +// 0 INVOKESTATIC UInt\$Erased.box +// 1 INVOKEVIRTUAL UInt.unbox // 0 valueOf // 0 intValue \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt index 36026107ab4..fa778777dc4 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/boxUnboxOnInlinedParameters.kt @@ -9,17 +9,17 @@ fun T.idExtension(): T = this inline fun T.inlinedIdExtension(): T = this fun test(f: Foo) { - inlinedId(f) // box + inlinedId(f) inlinedId(f).idExtension() // box - f.inlinedIdExtension() // box + f.inlinedIdExtension() 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 INVOKEVIRTUAL Foo.unbox +// 2 INVOKESTATIC Foo\$Erased.box +// 1 INVOKEVIRTUAL Foo.unbox // 0 valueOf // 0 intValue \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt index 285b10cf9e3..3a5478f1380 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/inlineClassBoxingOnAssignment.kt @@ -7,39 +7,39 @@ inline class InlineNullableReference(val a: Any?) fun test1(a: InlineNotNullPrimitive) { val a0 = a - val a1: Any = a // box - val a2: Any? = a // box + val a1: Any = a + val a2: Any? = a val a3: InlineNotNullPrimitive = a - val a4: InlineNotNullPrimitive? = a // box + val a4: InlineNotNullPrimitive? = a } fun test2(b: InlineNullablePrimitive) { val b0 = b - val b1: Any = b // box - val b2: Any? = b // box + val b1: Any = b + val b2: Any? = b val b3: InlineNullablePrimitive = b - val b4: InlineNullablePrimitive? = b // box + val b4: InlineNullablePrimitive? = b } fun test3(c: InlineNotNullReference) { val c0 = c - val c1: Any = c // box - val c2: Any? = c // box + val c1: Any = c + val c2: Any? = c val c3: InlineNotNullReference = c val c4: InlineNotNullReference? = c } fun test4(d: InlineNullableReference) { val d0 = d - val d1: Any = d // box - val d2: Any? = d // box + val d1: Any = d + val d2: Any? = d val d3: InlineNullableReference = d - val d4: InlineNullableReference? = d // box + val d4: InlineNullableReference? = d } -// 3 INVOKESTATIC InlineNotNullPrimitive\$Erased.box -// 3 INVOKESTATIC InlineNullablePrimitive\$Erased.box -// 2 INVOKESTATIC InlineNotNullReference\$Erased.box -// 3 INVOKESTATIC InlineNullableReference\$Erased.box +// 0 INVOKESTATIC InlineNotNullPrimitive\$Erased.box +// 0 INVOKESTATIC InlineNullablePrimitive\$Erased.box +// 0 INVOKESTATIC InlineNotNullReference\$Erased.box +// 0 INVOKESTATIC InlineNullableReference\$Erased.box // 0 valueOf \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt b/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt index 4467a309403..3a866468394 100644 --- a/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt +++ b/compiler/testData/codegen/bytecodeText/inlineClasses/uIntArraySwapBoxing.kt @@ -16,8 +16,8 @@ fun UIntArray.swap(i: Int, j: Int) { this[j] = this[i].also { this[i] = this[j] } } -// 2 INVOKEVIRTUAL UInt.unbox -// 1 INVOKESTATIC UInt\$Erased.box +// 0 INVOKEVIRTUAL UInt.unbox +// 0 INVOKESTATIC UInt\$Erased.box // 0 intValue // 0 valueOf \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index baf348cef74..6cfcc0d6491 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -424,6 +424,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { 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") public void testIntCompareTo() throws Exception { runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/intCompareTo.kt");