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
This commit is contained in:
+10
-5
@@ -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<SuspensionPoint>
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+32
@@ -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<out T> {
|
||||
suspend fun await(): T
|
||||
}
|
||||
|
||||
open class DeferredCoroutine<T> : Deferred<T> {
|
||||
override suspend fun await(): T = awaitInternal() as T
|
||||
}
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(Continuation(EmptyCoroutineContext) {
|
||||
it.getOrThrow()
|
||||
})
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
DeferredCoroutine<String>().await()
|
||||
}
|
||||
TailCallOptimizationChecker.checkNoStateMachineIn("await\$suspendImpl")
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user