diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt index a6194d3bdd0..6525870e03d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/captured/CapturedVarsOptimizationMethodTransformer.kt @@ -17,6 +17,7 @@ package org.jetbrains.kotlin.codegen.optimization.captured import org.jetbrains.kotlin.builtins.PrimitiveType +import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables import org.jetbrains.kotlin.codegen.optimization.fixStack.peek @@ -63,6 +64,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { val stackInsns: MutableCollection = LinkedHashSet() val getFieldInsns: MutableCollection = LinkedHashSet() val putFieldInsns: MutableCollection = LinkedHashSet() + var cleanVarInstruction: VarInsnNode? = null fun canRewrite(): Boolean = !hazard && @@ -198,6 +200,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { for (refValue in refValues) { if (refValue.hazard) continue val localVar = refValue.localVar ?: continue + val oldVarIndex = localVar.index if (refValue.valueType.size != 1) { refValue.localVarIndex = methodNode.maxLocals @@ -214,9 +217,30 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { refValue.hazard = true continue } + + val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions) + if (cleanInstructions.size > 1 ) { + refValue.hazard = true + continue + } + refValue.cleanVarInstruction = cleanInstructions.firstOrNull() } } + private fun findCleanInstructions(refValue: CapturedVarDescriptor, oldVarIndex: Int, instructions: InsnList): List { + val cleanInstructions = + InsnSequence(instructions).filterIsInstance().filter { + 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() + return cleanInstructions + } + private fun rewrite() { for (refValue in refValues) { if (!refValue.canRewrite()) continue @@ -248,6 +272,13 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() { capturedVar.putFieldInsns.forEach { set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex)) } + + //after visiting block codegen tries to delete all allocated references: + // see ExpressionCodegen.addLeaveTaskToRemoveLocalVariableFromFrameMap + capturedVar.cleanVarInstruction?.let { + remove(it.previous) + remove(it) + } } } } diff --git a/compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt b/compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt new file mode 100644 index 00000000000..0d223fc9f97 --- /dev/null +++ b/compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt @@ -0,0 +1,26 @@ +inline fun inlineCall(action: () -> Unit) { + action() +} + +fun test() { + var width = 1 + inlineCall { + width += width + } +} + +fun test2() { + var width = 1L + val newValue = 1; + val newValue2 = "123"; + val newValue3 = 2.0; + inlineCall { + width += width + } +} + +fun box(): String { + test() + test2() + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 771fc2a6df4..b448753c750 100644 --- a/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -3859,6 +3859,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes doTest(fileName); } + @TestMetadata("kt17200.kt") + public void testKt17200() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt"); + doTest(fileName); + } + @TestMetadata("sharedSlotsWithCapturedVars.kt") public void testSharedSlotsWithCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 39a84f7fc2b..6dbe21991cc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -3859,6 +3859,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("kt17200.kt") + public void testKt17200() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt"); + doTest(fileName); + } + @TestMetadata("sharedSlotsWithCapturedVars.kt") public void testSharedSlotsWithCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index c6991898fd2..0e9151b72bc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -3859,6 +3859,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes doTest(fileName); } + @TestMetadata("kt17200.kt") + public void testKt17200() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt"); + doTest(fileName); + } + @TestMetadata("sharedSlotsWithCapturedVars.kt") public void testSharedSlotsWithCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index fae7c286951..1df82a0d024 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -4532,6 +4532,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { doTest(fileName); } + @TestMetadata("kt17200.kt") + public void testKt17200() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt"); + doTest(fileName); + } + @TestMetadata("sharedSlotsWithCapturedVars.kt") public void testSharedSlotsWithCapturedVars() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");