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:
-27
@@ -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
|
||||
|
||||
-26
@@ -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
|
||||
|
||||
Vendored
-27
@@ -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
|
||||
|
||||
Vendored
-26
@@ -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
|
||||
|
||||
+7
-7
@@ -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")
|
||||
|
||||
+8
-8
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user