Perform bytecode optimisations for inline classes

#KT-23742 Fixed
This commit is contained in:
Mikhail Zarechenskiy
2018-06-15 15:36:47 +03:00
parent 7465289811
commit 01d9be65bc
16 changed files with 204 additions and 71 deletions
@@ -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
@@ -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)
}
@@ -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)
@@ -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()
@@ -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)
@@ -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)
@@ -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) {
@@ -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)
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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");