diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java index be7c8ae55e3..4d00b3d2c70 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/AsmUtil.java @@ -1081,27 +1081,23 @@ public class AsmUtil { } public static void pushDefaultValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) { - if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { - v.aconst(null); - } - else { - pushDefaultPrimitiveValueOnStack(type, v); - } + v.visitInsn(defaultValueOpcode(type)); } - public static void pushDefaultPrimitiveValueOnStack(@NotNull Type type, @NotNull InstructionAdapter v) { + public static int defaultValueOpcode(@NotNull Type type) { + if (type.getSort() == Type.OBJECT || type.getSort() == Type.ARRAY) { + return ACONST_NULL; + } if (type.getSort() == Type.FLOAT) { - v.fconst(0); + return FCONST_0; } - else if (type.getSort() == Type.DOUBLE) { - v.dconst(0); + if (type.getSort() == Type.DOUBLE) { + return DCONST_0; } - else if (type.getSort() == Type.LONG) { - v.lconst(0); - } - else { - v.iconst(0); + if (type.getSort() == Type.LONG) { + return LCONST_0; } + return ICONST_0; } public static boolean isInstancePropertyWithStaticBackingField(@NotNull PropertyDescriptor propertyDescriptor) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 21678617508..890af120b29 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -636,11 +636,8 @@ public abstract class StackValue { if (toType.equals(UNIT_TYPE) || toType.equals(OBJECT_TYPE)) { putUnitInstance(v); } - else if (toType.getSort() == Type.OBJECT || toType.getSort() == Type.ARRAY) { - v.aconst(null); - } else { - pushDefaultPrimitiveValueOnStack(toType, v); + pushDefaultValueOnStack(toType, v); } } else if (toType.equals(UNIT_TYPE)) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/CapturedVarsOptimizationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/CapturedVarsOptimizationMethodTransformer.kt index 7c6bfccc7eb..4ed2cac5348 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/CapturedVarsOptimizationMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/CapturedVarsOptimizationMethodTransformer.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.optimization import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.top @@ -207,13 +208,6 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { refValue.localVarIndex = localVar.index } - val startIndex = localVar.start.getIndex() - val initFieldInsns = refValue.putFieldInsns.filter { it.getIndex() < startIndex } - if (initFieldInsns.size != 1) { - refValue.hazard = true - continue - } - val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions) if (cleanInstructions.size > 1) { refValue.hazard = true @@ -227,14 +221,14 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { return InsnSequence(instructions).filterIsInstance().filter { it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex }.filter { - it.previous?.opcode == Opcodes.ACONST_NULL - }.filter { - val operationIndex = instructions.indexOf(it) - val localVariableNode = refValue.localVar!! - instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf( - localVariableNode.end - ) - }.toList() + it.previous?.opcode == Opcodes.ACONST_NULL + }.filter { + val operationIndex = instructions.indexOf(it) + val localVariableNode = refValue.localVar!! + instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf( + localVariableNode.end + ) + }.toList() } private fun rewrite() { @@ -250,9 +244,17 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { private fun rewriteRefValue(capturedVar: CapturedVarDescriptor) { methodNode.instructions.run { - capturedVar.localVar!!.let { - it.signature = null - it.desc = capturedVar.valueType.descriptor + val localVar = capturedVar.localVar!! + localVar.signature = null + localVar.desc = capturedVar.valueType.descriptor + + val loadOpcode = capturedVar.valueType.getOpcode(Opcodes.ILOAD) + val storeOpcode = capturedVar.valueType.getOpcode(Opcodes.ISTORE) + + if (capturedVar.putFieldInsns.none { it.getIndex() < localVar.start.getIndex() }) { + // variable needs to be initialized before its live range can begin + insertBefore(capturedVar.newInsn, InsnNode(AsmUtil.defaultValueOpcode(capturedVar.valueType))) + insertBefore(capturedVar.newInsn, VarInsnNode(storeOpcode, capturedVar.localVarIndex)) } remove(capturedVar.newInsn) @@ -260,14 +262,8 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { capturedVar.stackInsns.forEach { remove(it) } capturedVar.aloadInsns.forEach { remove(it) } capturedVar.astoreInsns.forEach { remove(it) } - - capturedVar.getFieldInsns.forEach { - set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ILOAD), capturedVar.localVarIndex)) - } - - capturedVar.putFieldInsns.forEach { - set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex)) - } + capturedVar.getFieldInsns.forEach { set(it, VarInsnNode(loadOpcode, capturedVar.localVarIndex)) } + capturedVar.putFieldInsns.forEach { set(it, VarInsnNode(storeOpcode, capturedVar.localVarIndex)) } //after visiting block codegen tries to delete all allocated references: // see ExpressionCodegen.addLeaveTaskToRemoveLocalVariableFromFrameMap diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt index b1ddc4e3cef..6c25a82f9a7 100644 --- a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInChainOfInlineFuns.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun test() { var x = 0 run { diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt index 07ac083dc2f..67ac4d25403 100644 --- a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedInInlineOnly.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun test() { var x = 0 run { ++x } diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedValInLambdaInitializedInside.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedValInLambdaInitializedInside.kt index 0c2d846bae2..4da5b6708e4 100644 --- a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedValInLambdaInitializedInside.kt +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedValInLambdaInitializedInside.kt @@ -1,11 +1,4 @@ // WITH_RUNTIME -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - -// In JVM IR, SharedVariablesLowering transforms `x` into a shared variable to be able to update it from a lambda, -// which is a separate function (...$lambda-0). -// If we keep the existing representation of lambda bodies as separate functions in JVM IR, the only viable option to fix this test -// seems to support this case in the bytecode optimization pass CapturedVarsOptimizationMethodTransformer. fun box(): String { val x: String diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt index d043c6121b0..209ca2934e0 100644 --- a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/capturedVarsOfSize2.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun box(): String { var xl = 0L // Long, size 2 var xi = 0 // Int, size 1 diff --git a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt index 79b12249537..c580dcb4e10 100644 --- a/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt +++ b/compiler/testData/codegen/bytecodeText/capturedVarsOptimization/withStackNormalization.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun add(x: Int, y: Int) = x + y fun test() { diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt index 5bc1fc26f92..f970e76ff01 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxing.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun test(): java.lang.Integer { val c: java.lang.Integer run { @@ -10,4 +7,9 @@ fun test(): java.lang.Integer { } // 2 ASTORE 0 -// 1 LOCALVARIABLE c Ljava/lang/Integer; L1 L.* 0 \ No newline at end of file + +// JVM_TEMPLATES +// 1 LOCALVARIABLE c Ljava/lang/Integer; + +// JVM_IR_TEMPLATES +// 1 LOCALVARIABLE c Ljava/lang/Object; \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxingVar.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxingVar.kt index c2810b191ab..aea32188699 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxingVar.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/boxingVar.kt @@ -6,5 +6,5 @@ fun test(): java.lang.Integer { return c } -// 1 ASTORE 0 -// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$ObjectRef; L1 L.* 0 \ No newline at end of file +// 2 ASTORE 0 +// 1 LOCALVARIABLE c Ljava/lang/Object; \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt index 8ccb7758d26..a9b608e0948 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/generics.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR - inline fun foo(default: T): T { val t: T run { @@ -13,6 +11,6 @@ fun test() { } // two in foo and two in test - // 4 ASTORE 2 -// 1 LOCALVARIABLE t\$iv Ljava/lang/Object; L3 L.* 2 \ No newline at end of file +// 1 LOCALVARIABLE t Ljava/lang/Object; +// 1 LOCALVARIABLE t\$iv Ljava/lang/Object; \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt index 0d3288e7e53..7f346a34231 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/genericsVar.kt @@ -1,4 +1,3 @@ - inline fun foo(default: T): T { var t: T run { @@ -12,6 +11,6 @@ fun test() { } // two in foo and two in test - -// 2 ASTORE 2 -// 1 LOCALVARIABLE t\$iv Lkotlin/jvm/internal/Ref\$ObjectRef; L3 L.* 2 \ No newline at end of file +// 4 ASTORE 2 +// 1 LOCALVARIABLE t Ljava/lang/Object; +// 1 LOCALVARIABLE t\$iv Ljava/lang/Object; \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt index 6243a6fff2a..4ca13901fb5 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClass.kt @@ -1,5 +1,5 @@ // IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR +// TODO: JVM_IR uses ObjectRef instead of IntRef for the value fun test(): UInt { val c: UInt @@ -10,4 +10,4 @@ fun test(): UInt { } // 2 ISTORE 0 -// 1 LOCALVARIABLE c I L1 L.* 0 \ No newline at end of file +// 1 LOCALVARIABLE c I \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClassVar.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClassVar.kt index 7d48d568811..60e26da1c20 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClassVar.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/inlineClassVar.kt @@ -1,5 +1,5 @@ // IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR +// TODO: JVM_IR uses ObjectRef instead of IntRef for the value fun test(): UInt { var c: UInt @@ -9,5 +9,5 @@ fun test(): UInt { return c } -// 1 ASTORE 0 -// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$IntRef; L1 L.* 0 \ No newline at end of file +// 2 ISTORE 0 +// 1 LOCALVARIABLE c I \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt index 2be1fc776bc..0f571dc0bd8 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/lateinit.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun test(): Char { lateinit var c: Any run { @@ -9,5 +6,11 @@ fun test(): Char { return c as Char } +// 1 LOCALVARIABLE c Ljava/lang/Object; + +// JVM_TEMPLATES // 2 ASTORE 0 -// 1 LOCALVARIABLE c Ljava/lang/Object; L1 L.* 0 \ No newline at end of file + +// JVM_IR_TEMPLATES +// 3 ASTORE 0 +// *two* of them are after the start of c's live range \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt index c2e121915d2..d9bd968e7d6 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/run.kt @@ -1,6 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36648 Captured variables not optimized in JVM_IR - fun test(): Char { val c: Char run { @@ -12,4 +9,4 @@ fun test(): Char { // The first on declaration, the other on initialization // 2 ISTORE 0 -// 1 LOCALVARIABLE c C L1 L.* 0 \ No newline at end of file +// 1 LOCALVARIABLE c C \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/localInitializationLVT/runVar.kt b/compiler/testData/codegen/bytecodeText/localInitializationLVT/runVar.kt index 3717d6603a1..a21962ee8d7 100644 --- a/compiler/testData/codegen/bytecodeText/localInitializationLVT/runVar.kt +++ b/compiler/testData/codegen/bytecodeText/localInitializationLVT/runVar.kt @@ -6,5 +6,5 @@ fun test(): Char { return c } -// 1 ASTORE 0 -// 1 LOCALVARIABLE c Lkotlin/jvm/internal/Ref\$CharRef; L1 L.* 0 \ No newline at end of file +// 2 ISTORE 0 +// 1 LOCALVARIABLE c C \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedOnce.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedOnce.kt index 191ef119ebe..385f2a0ebba 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedOnce.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/checkedOnce.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36813 Support code generated by JVM_IR in redundant null check optimization fun almostAlwaysTrue() = true fun test() { diff --git a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt index 0c7a4b266c8..b73f0d43670 100644 --- a/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt +++ b/compiler/testData/codegen/bytecodeText/nullCheckOptimization/localLateinit/initialized.kt @@ -1,5 +1,3 @@ -// IGNORE_BACKEND: JVM_IR -// TODO KT-36813 Support code generated by JVM_IR in redundant null check optimization fun test() { lateinit var z: String run {