From f760cd6736c7a6405945bc6151637827b597f9f1 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Fri, 8 Oct 2021 17:20:27 +0200 Subject: [PATCH] Make CHECKCAST Object not break tail-call optimization Since CHECKCAST Object does nothing for return value of suspend function - the function returns references only, this is safe. #KT-49157 Fixed --- .../coroutines/TailCallOptimization.kt | 15 ++++++--- .../FirBlackBoxCodegenTestGenerated.java | 6 ++++ .../deferredAwaitSuspendImpl.kt | 32 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 6 ++++ .../IrBlackBoxCodegenTestGenerated.java | 6 ++++ .../LightAnalysisModeTestGenerated.java | 5 +++ 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/TailCallOptimization.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/TailCallOptimization.kt index fc19055a7c4..7b640441ec9 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/TailCallOptimization.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/TailCallOptimization.kt @@ -17,10 +17,7 @@ import org.jetbrains.kotlin.utils.sure import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.LineNumberNode -import org.jetbrains.org.objectweb.asm.tree.MethodNode -import org.jetbrains.org.objectweb.asm.tree.VarInsnNode +import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.BasicInterpreter import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.Frame @@ -155,7 +152,7 @@ internal class MethodNodeExaminer( } if (!insn.isMeaningful || insn.opcode in SAFE_OPCODES || insn.isInvisibleInDebugVarInsn(methodNode) || isInlineMarker(insn) - || insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance() + || insn.isSafeUnitInstance() || insn.isAreturnAfterSafeUnitInstance() || insn.isCheckcastObject() ) { setOf() } else null @@ -187,6 +184,9 @@ internal class MethodNodeExaminer( } } +private fun AbstractInsnNode.isCheckcastObject(): Boolean = + opcode == Opcodes.CHECKCAST && (this as TypeInsnNode).desc == AsmTypes.OBJECT_TYPE.internalName + private fun AbstractInsnNode?.isInvisibleInDebugVarInsn(methodNode: MethodNode): Boolean { val insns = methodNode.instructions val index = insns.indexOf(this) @@ -225,6 +225,11 @@ private class TcoInterpreter(private val suspensionPoints: List } override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { + // Assume, that CHECKCAST Object does not break tail-call optimization + // TODO: Investigate, whether any CHECKCAST is safe in terms of tail-call optimization + if (value is FromSuspensionPointValue && insn.isCheckcastObject()) { + return value + } return super.unaryOperation(insn, value).convert(insn) } diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index 694871dbd57..1cfb31a9eee 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -12241,6 +12241,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); } + @Test + @TestMetadata("deferredAwaitSuspendImpl.kt") + public void testDeferredAwaitSuspendImpl() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt"); + } + @Test @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt new file mode 100644 index 00000000000..742fefd94b6 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// FULL_JDK +// WITH_RUNTIME +// WITH_COROUTINES +// CHECK_TAIL_CALL_OPTIMIZATION +import helpers.* +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +suspend fun awaitInternal(): Any? = TailCallOptimizationChecker.saveStackTrace() + +interface Deferred { + suspend fun await(): T +} + +open class DeferredCoroutine : Deferred { + override suspend fun await(): T = awaitInternal() as T +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(Continuation(EmptyCoroutineContext) { + it.getOrThrow() + }) +} + +fun box(): String { + builder { + DeferredCoroutine().await() + } + TailCallOptimizationChecker.checkNoStateMachineIn("await\$suspendImpl") + return "OK" +} \ No newline at end of file diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 62a8333d69c..3b8d6f562e3 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -12163,6 +12163,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); } + @Test + @TestMetadata("deferredAwaitSuspendImpl.kt") + public void testDeferredAwaitSuspendImpl() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt"); + } + @Test @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 2ac764880bb..054f08158ba 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -12241,6 +12241,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); } + @Test + @TestMetadata("deferredAwaitSuspendImpl.kt") + public void testDeferredAwaitSuspendImpl() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt"); + } + @Test @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index c2a7fc04310..3ff47410d54 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -9784,6 +9784,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/crossinline.kt"); } + @TestMetadata("deferredAwaitSuspendImpl.kt") + public void testDeferredAwaitSuspendImpl() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/deferredAwaitSuspendImpl.kt"); + } + @TestMetadata("inlineWithStateMachine.kt") public void testInlineWithStateMachine() throws Exception { runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithStateMachine.kt");