JVM: Check for multiple {POP, Unit} sequences in suspend function TCO
We already check for {POP, Unit} sequence before ARETURN, but if the
there are multiple sequence before ARETURN, the compiler assumes, that
TCO misses.
The fix is to check, that the instruction after the sequence is either
ARETURN or another {POP, Unit} sequence.
#KT-50835
#KT-54152 Fixed
This commit is contained in:
+1
-1
@@ -87,7 +87,7 @@ private val AbstractInsnNode.nextMeaningful: AbstractInsnNode?
|
||||
get() = next.skipUntilMeaningful()
|
||||
|
||||
private val AbstractInsnNode.isReturnUnit: Boolean
|
||||
get() = isUnitInstance() && nextMeaningful?.opcode == Opcodes.ARETURN
|
||||
get() = isUnitInstance() && nextMeaningful?.let { it.opcode == Opcodes.ARETURN || it.isPopBeforeReturnUnit } == true
|
||||
|
||||
private val AbstractInsnNode.isPopBeforeReturnUnit: Boolean
|
||||
get() = opcode == Opcodes.POP && nextMeaningful?.isReturnUnit == true
|
||||
|
||||
+6
@@ -13026,6 +13026,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaParameterInlining.kt")
|
||||
public void testLambdaParameterInlining() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambdaParameterInlining.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnInlineClass.kt")
|
||||
public void testReturnInlineClass() throws Exception {
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// TARGET_BACKEND: JVM
|
||||
// FULL_JDK
|
||||
// WITH_STDLIB
|
||||
// WITH_COROUTINES
|
||||
// CHECK_TAIL_CALL_OPTIMIZATION
|
||||
|
||||
import helpers.*
|
||||
import kotlin.coroutines.*
|
||||
import kotlin.coroutines.intrinsics.*
|
||||
|
||||
fun assert(value: () -> Boolean) {}
|
||||
|
||||
class ChannelSegment<E>(val id: Long)
|
||||
|
||||
suspend fun suspendHere() = suspendCoroutineUninterceptedOrReturn<Unit> { x ->
|
||||
TailCallOptimizationChecker.saveStackTrace(x)
|
||||
COROUTINE_SUSPENDED
|
||||
}
|
||||
|
||||
private const val RESULT_SUSPEND_NO_WAITER = 3
|
||||
|
||||
open class BufferedChannel<E> {
|
||||
private val sendSegment = ChannelSegment<E>(0)
|
||||
|
||||
suspend fun send(element: E): Unit =
|
||||
sendImpl(
|
||||
onClosed = {},
|
||||
onNoWaiterSuspend = { sendOnNoWaiterSuspend() }
|
||||
)
|
||||
|
||||
private suspend fun sendOnNoWaiterSuspend() {
|
||||
suspendHere()
|
||||
}
|
||||
|
||||
private fun findSegmentSend(): ChannelSegment<E>? = null
|
||||
|
||||
private fun updateCellSend(): Int = RESULT_SUSPEND_NO_WAITER
|
||||
|
||||
private inline fun <R> sendImpl(
|
||||
onClosed: () -> R,
|
||||
onNoWaiterSuspend: () -> R = { error("unexpected") }
|
||||
): R {
|
||||
var segment = sendSegment
|
||||
while (true) {
|
||||
val closed = false
|
||||
val id = 0L
|
||||
if (segment.id != id) {
|
||||
assert { segment.id < id }
|
||||
// Find the required segment.
|
||||
segment = null ?:
|
||||
if (closed) return onClosed() else continue
|
||||
}
|
||||
when(updateCellSend()) {
|
||||
RESULT_SUSPEND_NO_WAITER -> {
|
||||
return onNoWaiterSuspend()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(EmptyContinuation)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
BufferedChannel<Int>().send(0)
|
||||
}
|
||||
TailCallOptimizationChecker.checkNoStateMachineIn("send")
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -12858,6 +12858,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaParameterInlining.kt")
|
||||
public void testLambdaParameterInlining() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambdaParameterInlining.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnInlineClass.kt")
|
||||
public void testReturnInlineClass() throws Exception {
|
||||
|
||||
+6
@@ -13026,6 +13026,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("lambdaParameterInlining.kt")
|
||||
public void testLambdaParameterInlining() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambdaParameterInlining.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
@TestMetadata("returnInlineClass.kt")
|
||||
public void testReturnInlineClass() throws Exception {
|
||||
|
||||
+5
@@ -10371,6 +10371,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/interfaceDelegation.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaParameterInlining.kt")
|
||||
public void testLambdaParameterInlining() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/lambdaParameterInlining.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("returnInlineClass.kt")
|
||||
public void testReturnInlineClass() throws Exception {
|
||||
runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/returnInlineClass.kt");
|
||||
|
||||
Reference in New Issue
Block a user