Implement unit suspend functions tail-call optimisation

Unlike previously, this optimisation works on every callee return type.
Tail-calls inside unit functions can be either
INVOKE...
ARETURN
or
INVOKE
POP
GETSTATIC kotlin/Unit.INSTANCE
ARETURN
The first pattern is already covered. The second one is a bit tricky,
since we cannot just assume than the function is tail-call, we also need
to check whether the callee returned COROUTINE_SUSPENDED marker.
Thus, resulting bytecode of function's 'epilogue' look like
DUP
INVOKESTATIC getCOROUTINE_SUSPENDED
IF_ACMPNE LN
ARETURN
LN:
POP

 #KT-28938 Fixed
This commit is contained in:
Ilmir Usmanov
2019-07-23 17:32:00 +03:00
parent a16e03681b
commit cc06798e2c
13 changed files with 156 additions and 327 deletions
@@ -17,12 +17,7 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1$lambda$1
@@ -102,28 +97,11 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 {
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1
public method <init>(p0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2 {
synthetic final field $this_source$inlined: Sink
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2
inner class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$2$1
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1): void
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
@@ -205,12 +183,7 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1$lambda$1
@@ -17,29 +17,11 @@ public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$1 {
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
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
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1
public method <init>(p0: CrossinlineKt$box$1$doResume$$inlined$filter$1$2, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class CrossinlineKt$box$1$doResume$$inlined$filter$1$2 {
synthetic final field $this_source$inlined: Sink
synthetic final field this$0: CrossinlineKt$box$1$doResume$$inlined$filter$1
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2
inner class CrossinlineKt$box$1$doResume$$inlined$filter$1$2$1
public method <init>(p0: Sink, p1: CrossinlineKt$box$1$doResume$$inlined$filter$1): void
public method close(@org.jetbrains.annotations.Nullable p0: java.lang.Throwable): void
public @org.jetbrains.annotations.Nullable method send(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
@@ -84,10 +66,6 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 {
@kotlin.Metadata
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
@@ -212,10 +190,6 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
@kotlin.Metadata
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
@@ -26,26 +26,9 @@ public final class flow/InnerObjectRetransformationKt$check$$inlined$collect$1 {
public @org.jetbrains.annotations.Nullable method emit(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$flow$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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1$1
public method <init>(p0: flow.InnerObjectRetransformationKt$check$$inlined$flow$1, p1: kotlin.coroutines.Continuation): void
public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object
}
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$flow$1 {
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1$1
public method <init>(): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object
}
@@ -112,12 +95,7 @@ public final class flow/InnerObjectRetransformationKt$flow$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$1
@@ -139,12 +117,7 @@ public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1 {
}
@kotlin.Metadata
@kotlin.coroutines.jvm.internal.DebugMetadata
public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$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
field label: int
synthetic field result: java.lang.Object
synthetic final field this$0: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$2
@@ -24,27 +24,9 @@ public final class flow/InnerObjectRetransformationKt$check$$inlined$collect$1 {
public @org.jetbrains.annotations.Nullable method emit(@org.jetbrains.annotations.NotNull p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$flow$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: flow.InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1$1
public method <init>(p0: flow.InnerObjectRetransformationKt$check$$inlined$flow$1, p1: kotlin.coroutines.experimental.Continuation): void
public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object
synthetic final method getLabel(): int
synthetic final method setLabel(p0: int): void
}
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$check$$inlined$flow$1 {
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1
inner class flow/InnerObjectRetransformationKt$check$$inlined$flow$1$1
public method <init>(): void
public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: flow.FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object
}
@@ -117,10 +99,6 @@ public final class flow/InnerObjectRetransformationKt$flow$1 {
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$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: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$1
@@ -145,10 +123,6 @@ public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$1 {
@kotlin.Metadata
public final class flow/InnerObjectRetransformationKt$flowWith$$inlined$flow$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: flow.InnerObjectRetransformationKt$flowWith$$inlined$flow$2
@@ -17,9 +17,9 @@ suspend fun lambdaAsParameterNotTailCall(c: suspend ()->Unit) { c(); c() }
suspend fun lambdaAsParameterReturn(c: suspend ()->Unit) { return c() }
suspend fun returnsInt() = 42.also { TailCallOptimizationChecker.saveStackTrace() }
// This should not be tail-call, since the caller should push Unit.INSTANCE on stack
suspend fun callsIntNotTailCall() { returnsInt() }
suspend fun callsIntTailCall() { returnsInt() }
suspend fun multipleExitPoints(b: Boolean) { if (b) empty() else withoutReturn() }
suspend fun multipleExitPointsNotTailCall(b: Boolean) { if (b) empty() else returnsInt() }
suspend fun multipleExitPointsTailCall(b: Boolean) { if (b) empty() else returnsInt() }
fun ordinary() = 1
inline fun ordinaryInline() { ordinary() }
@@ -95,14 +95,14 @@ fun box(): String {
lambdaAsParameterReturn { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameterReturn")
callsIntNotTailCall()
TailCallOptimizationChecker.checkStateMachineIn("callsIntNotTailCall")
callsIntTailCall()
TailCallOptimizationChecker.checkNoStateMachineIn("callsIntTailCall")
multipleExitPoints(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPoints")
multipleExitPointsNotTailCall(false)
TailCallOptimizationChecker.checkStateMachineIn("multipleExitPointsNotTailCall")
multipleExitPointsTailCall(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsTailCall")
multipleExitPointsWithOrdinaryInline(true)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWithOrdinaryInline")
@@ -120,7 +120,7 @@ fun box(): String {
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericInferType")
useNullableUnit()
TailCallOptimizationChecker.checkStateMachineIn("useNullableUnit")
TailCallOptimizationChecker.checkNoStateMachineIn("useNullableUnit")
useRunRunRunRunRun()
TailCallOptimizationChecker.checkNoStateMachineIn("useRunRunRunRunRun")
@@ -40,8 +40,8 @@ suspend fun lambdaAsParameterReturn(c: suspend () -> Unit) {
}
suspend fun returnsInt() = 42.also { TailCallOptimizationChecker.saveStackTrace() }
// This should not be tail-call, since the caller should push Unit.INSTANCE on stack
suspend fun callsIntNotTailCall() {
suspend fun callsIntTailCall() {
returnsInt()
return
empty()
@@ -53,7 +53,7 @@ suspend fun multipleExitPoints(b: Boolean) {
empty()
}
suspend fun multipleExitPointsNotTailCall(b: Boolean) {
suspend fun multipleExitPointsTailCall(b: Boolean) {
if (b) empty() else returnsInt()
return
empty()
@@ -153,14 +153,14 @@ fun box(): String {
lambdaAsParameterReturn { TailCallOptimizationChecker.saveStackTrace() }
TailCallOptimizationChecker.checkNoStateMachineIn("lambdaAsParameterReturn")
callsIntNotTailCall()
TailCallOptimizationChecker.checkStateMachineIn("callsIntNotTailCall")
callsIntTailCall()
TailCallOptimizationChecker.checkNoStateMachineIn("callsIntTailCall")
multipleExitPoints(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPoints")
multipleExitPointsNotTailCall(false)
TailCallOptimizationChecker.checkStateMachineIn("multipleExitPointsNotTailCall")
multipleExitPointsTailCall(false)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsTailCall")
multipleExitPointsWithOrdinaryInline(true)
TailCallOptimizationChecker.checkNoStateMachineIn("multipleExitPointsWithOrdinaryInline")
@@ -178,7 +178,7 @@ fun box(): String {
TailCallOptimizationChecker.checkNoStateMachineIn("useGenericInferType")
useNullableUnit()
TailCallOptimizationChecker.checkStateMachineIn("useNullableUnit")
TailCallOptimizationChecker.checkNoStateMachineIn("useNullableUnit")
useRunRunRunRunRun()
TailCallOptimizationChecker.checkNoStateMachineIn("useRunRunRunRunRun")