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 =
|
||||
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
|
||||
|
||||
+16
-6
@@ -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<AbstractInsnNode>()
|
||||
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
|
||||
@@ -63,7 +66,7 @@ class BoxedValueDescriptor(
|
||||
private val mergedWith = HashSet<BoxedValueDescriptor>()
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
+50
-11
@@ -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<Int, BoxedBasicValue>()
|
||||
|
||||
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<BasicValue>) =
|
||||
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<BasicValue>): Boolean =
|
||||
values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers }
|
||||
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>, generationState: GenerationState): Boolean =
|
||||
values.none { getUnboxedType(it.type, generationState) in shouldUseEqualsForWrappers }
|
||||
|
||||
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
|
||||
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
|
||||
|
||||
+5
-1
@@ -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()
|
||||
|
||||
|
||||
+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.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)
|
||||
|
||||
+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.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)
|
||||
|
||||
+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.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<AbstractInsnNode, StrictBasicValue> {
|
||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
|
||||
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter(generationState))
|
||||
val insns = methodNode.instructions.toArray()
|
||||
val nullabilityMap = LinkedHashMap<AbstractInsnNode, StrictBasicValue>()
|
||||
for (i in insns.indices) {
|
||||
|
||||
+15
-1
@@ -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 <T> getAndCheck(getFirst: () -> T, getSecond: () -> T) =
|
||||
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 {
|
||||
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
|
||||
|
||||
+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
|
||||
}
|
||||
|
||||
// 1 INVOKESTATIC UInt\$Erased.box
|
||||
// 0 INVOKESTATIC UInt\$Erased.box
|
||||
// 0 INVOKEVIRTUAL UInt.unbox
|
||||
|
||||
// 0 valueOf
|
||||
|
||||
Vendored
+2
-2
@@ -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
|
||||
+5
-5
@@ -9,17 +9,17 @@ fun <T> T.idExtension(): T = this
|
||||
inline fun <T> 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
|
||||
+15
-15
@@ -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
|
||||
+2
-2
@@ -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
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user