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:
Ilmir Usmanov
2021-10-08 17:20:27 +02:00
committed by Space
parent 4a99f04b41
commit f760cd6736
6 changed files with 65 additions and 5 deletions
@@ -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)
}
@@ -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 {
@@ -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"
}
@@ -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 {
@@ -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 {
@@ -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");