diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 2529e1a80f2..4d634d7e22e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -709,7 +709,7 @@ private class SuspensionPoint( } } -private fun getLastParameterIndex(desc: String, access: Int) = +internal fun getLastParameterIndex(desc: String, access: Int) = Type.getArgumentTypes(desc).dropLast(1).map { it.size }.sum() + (if (!isStatic(access)) 1 else 0) private fun getParameterTypesForCoroutineConstructor(desc: String, hasDispatchReceiver: Boolean, thisName: String) = @@ -842,7 +842,7 @@ private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): private val SAFE_OPCODES = ((Opcodes.DUP..Opcodes.DUP2_X2) + Opcodes.NOP + Opcodes.POP + Opcodes.POP2 + (Opcodes.IFEQ..Opcodes.GOTO)).toSet() -private fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) { +internal fun replaceFakeContinuationsWithRealOnes(methodNode: MethodNode, continuationIndex: Int) { val fakeContinuations = methodNode.instructions.asSequence().filter(::isFakeContinuationMarker).toList() for (fakeContinuation in fakeContinuations) { methodNode.instructions.removeAll(listOf(fakeContinuation.previous.previous, fakeContinuation.previous)) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt index 1b0d248fd6f..aa60de29d0e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/RedundantLocalsEliminationMethodTransformer.kt @@ -67,23 +67,31 @@ class RedundantLocalsEliminationMethodTransformer(private val languageVersionSet private fun removeWithReplacement( methodNode: MethodNode ): Boolean { - val insns = findSafeAstorePredecessors(methodNode, ignoreLocalVariableTable = false) { + val cfg = ControlFlowGraph.build(methodNode) + val insns = findSafeAstorePredecessors(methodNode, cfg, ignoreLocalVariableTable = false) { it.isUnitInstance() || it.opcode == Opcodes.ACONST_NULL || it.opcode == Opcodes.ALOAD } - insns.asIterable().firstOrNull { (pred, astore) -> - val index = astore.localIndex() + for ((pred, astore) in insns) { + val aload = findSingleLoadFromAstore(astore, cfg, methodNode) ?: continue methodNode.instructions.removeAll(listOf(pred, astore)) - - methodNode.instructions.asSequence() - .filter { it.opcode == Opcodes.ALOAD && it.localIndex() == index } - .toList() - .forEach { methodNode.instructions.set(it, pred.clone()) } + methodNode.instructions.set(aload, pred.clone()) return true } return false } + private fun findSingleLoadFromAstore( + astore: AbstractInsnNode, + cfg: ControlFlowGraph, + methodNode: MethodNode + ): AbstractInsnNode? { + val aload = methodNode.instructions.asSequence() + .singleOrNull { it.opcode == Opcodes.ALOAD && it.localIndex() == astore.localIndex() } ?: return null + val succ = findImmediateSuccessors(astore, cfg, methodNode).singleOrNull() ?: return null + return if (aload == succ) aload else null + } + private fun AbstractInsnNode.clone() = when (this) { is FieldInsnNode -> FieldInsnNode(opcode, owner, name, desc) is VarInsnNode -> VarInsnNode(opcode, `var`) @@ -143,37 +151,34 @@ class RedundantLocalsEliminationMethodTransformer(private val languageVersionSet private fun removeAloadCheckcastContinuationAstore(methodNode: MethodNode, languageVersionSettings: LanguageVersionSettings): Boolean { // Here we ignore the duplicates of continuation in local variable table, // Since it increases performance greatly. - val insns = findSafeAstorePredecessors(methodNode, ignoreLocalVariableTable = true) { + val cfg = ControlFlowGraph.build(methodNode) + val insns = findSafeAstorePredecessors(methodNode, cfg, ignoreLocalVariableTable = true) { it.opcode == Opcodes.CHECKCAST && (it as TypeInsnNode).desc == languageVersionSettings.continuationAsmType().internalName && it.previous?.opcode == Opcodes.ALOAD } + var changed = false + for ((checkcast, astore) in insns) { - val aload = checkcast.previous - val index = astore.localIndex() + val aloadk = checkcast.previous + val aloadn = findSingleLoadFromAstore(astore, cfg, methodNode) ?: continue - methodNode.instructions.removeAll(listOf(aload, checkcast, astore)) - - methodNode.instructions.asSequence() - .filter { it.opcode == Opcodes.ALOAD && it.localIndex() == index } - .toList() - .forEach { - methodNode.instructions.insertBefore(it, aload.clone()) - methodNode.instructions.set(it, checkcast.clone()) - } + methodNode.instructions.removeAll(listOf(aloadk, checkcast, astore)) + methodNode.instructions.insertBefore(aloadn, aloadk.clone()) + methodNode.instructions.set(aloadn, checkcast.clone()) + changed = true } - return insns.isNotEmpty() + return changed } private fun findSafeAstorePredecessors( methodNode: MethodNode, + cfg: ControlFlowGraph, ignoreLocalVariableTable: Boolean, predicate: (AbstractInsnNode) -> Boolean ): Map { val insns = methodNode.instructions.asSequence().filter { predicate(it) }.toList() - - val cfg = ControlFlowGraph.build(methodNode) val res = hashMapOf() for (insn in insns) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt index 2752aa77641..5a2b2dfb760 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -9,9 +9,7 @@ import com.intellij.util.ArrayUtil import org.jetbrains.kotlin.codegen.AsmUtil import org.jetbrains.kotlin.codegen.ClassBuilder import org.jetbrains.kotlin.codegen.StackValue -import org.jetbrains.kotlin.codegen.coroutines.CoroutineTransformerMethodVisitor -import org.jetbrains.kotlin.codegen.coroutines.isCoroutineSuperClass -import org.jetbrains.kotlin.codegen.coroutines.isResumeImplMethodName +import org.jetbrains.kotlin.codegen.coroutines.* import org.jetbrains.kotlin.codegen.optimization.common.asSequence import org.jetbrains.kotlin.codegen.serialization.JvmCodegenStringTable import org.jetbrains.kotlin.codegen.writeKotlinMetadata @@ -179,6 +177,12 @@ class AnonymousObjectTransformer( } deferringMethods.forEach { method -> + replaceFakeContinuationsWithRealOnes( + method.intermediate, + if (!inliningContext.isContinuation) + getLastParameterIndex(method.intermediate.desc, method.intermediate.access) + else 0 + ) removeFinallyMarkers(method.intermediate) method.visitEnd() diff --git a/compiler/testData/codegen/box/coroutines/kt25912.kt b/compiler/testData/codegen/box/coroutines/kt25912.kt new file mode 100644 index 00000000000..e6d5b3290c1 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/kt25912.kt @@ -0,0 +1,23 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// COMMON_COROUTINES_TEST +// IGNORE_BACKEND: JS_IR, JVM_IR, JS, NATIVE + +import helpers.* +import COROUTINES_PACKAGE.* + +fun box(): String { + val gg = object : Grouping { + override fun sourceIterator(): Iterator = listOf(1).iterator() + override fun keyOf(element: Int): String = "OK" + } + + var res = "" + suspend { + for (e in gg.sourceIterator()) { + val key = gg.keyOf(e) + res += key + } + }.startCoroutine(EmptyContinuation) + return res +} diff --git a/compiler/testData/codegen/box/coroutines/noStdLib/crossinline.txt b/compiler/testData/codegen/box/coroutines/noStdLib/crossinline.txt index b30b07843be..480fd1ff5d7 100644 --- a/compiler/testData/codegen/box/coroutines/noStdLib/crossinline.txt +++ b/compiler/testData/codegen/box/coroutines/noStdLib/crossinline.txt @@ -20,6 +20,7 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 @@ -94,6 +95,7 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 @@ -193,6 +195,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1 diff --git a/compiler/testData/codegen/box/coroutines/noStdLib/crossinline_1_2.txt b/compiler/testData/codegen/box/coroutines/noStdLib/crossinline_1_2.txt index 47d2137ba05..2f4c7a4d43d 100644 --- a/compiler/testData/codegen/box/coroutines/noStdLib/crossinline_1_2.txt +++ b/compiler/testData/codegen/box/coroutines/noStdLib/crossinline_1_2.txt @@ -22,6 +22,7 @@ public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2 @@ -87,6 +88,7 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 @@ -206,6 +208,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1 diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt index 2c93d7524e7..e14d7a66948 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.txt @@ -20,6 +20,7 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 @@ -94,6 +95,7 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 @@ -193,6 +195,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object field label: int synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1 diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_1_2.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_1_2.txt index e27d95ea936..1a02e28e5fd 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_1_2.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline_1_2.txt @@ -22,6 +22,7 @@ public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2 @@ -87,6 +88,7 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1 @@ -206,6 +208,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$lambda$1$1 { field L$0: java.lang.Object field L$1: java.lang.Object field L$2: java.lang.Object + field L$3: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1 diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt index 83e2bc48fe3..dbfa4bec29b 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.txt @@ -15,6 +15,7 @@ final class InlineWithoutStateMachineKt$box$1 { @kotlin.Metadata final class InlineWithoutStateMachineKt$complexSuspend$1 { field L$0: java.lang.Object + field L$1: java.lang.Object synthetic field data: java.lang.Object field label: int inner class InlineWithoutStateMachineKt$complexSuspend$1 diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_1_2.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_1_2.txt index 302b5c3cfdb..24265dce0a9 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_1_2.txt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_1_2.txt @@ -14,6 +14,7 @@ final class InlineWithoutStateMachineKt$box$1 { @kotlin.Metadata final class InlineWithoutStateMachineKt$complexSuspend$1 { field L$0: java.lang.Object + field L$1: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable inner class InlineWithoutStateMachineKt$complexSuspend$1 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 5a856e00134..8e0e7ab0dec 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -5745,6 +5745,16 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt21605.kt", "kotlin.coroutines"); } + @TestMetadata("kt25912.kt") + public void testKt25912_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("kt25912.kt") + public void testKt25912_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt", "kotlin.coroutines.experimental"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 9c83dcfff9d..33a7be294f8 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -5745,6 +5745,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt21605.kt", "kotlin.coroutines"); } + @TestMetadata("kt25912.kt") + public void testKt25912_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("kt25912.kt") + public void testKt25912_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt", "kotlin.coroutines.experimental"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 19214ea88e7..3690d42e33d 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -5745,6 +5745,16 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt21605.kt", "kotlin.coroutines"); } + @TestMetadata("kt25912.kt") + public void testKt25912_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("kt25912.kt") + public void testKt25912_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt", "kotlin.coroutines.experimental"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java index babb2dc0ae8..32963b899e2 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/IrJsCodegenBoxTestGenerated.java @@ -5270,6 +5270,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt21605.kt", "kotlin.coroutines.experimental"); } + @TestMetadata("kt25912.kt") + public void testKt25912_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines.experimental"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt", "kotlin.coroutines.experimental"); 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 5406d003227..7158642e175 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 @@ -5485,6 +5485,16 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt21605.kt", "kotlin.coroutines"); } + @TestMetadata("kt25912.kt") + public void testKt25912_1_2() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines.experimental"); + } + + @TestMetadata("kt25912.kt") + public void testKt25912_1_3() throws Exception { + runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/kt25912.kt", "kotlin.coroutines"); + } + @TestMetadata("lastExpressionIsLoop.kt") public void testLastExpressionIsLoop_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/lastExpressionIsLoop.kt", "kotlin.coroutines.experimental");