[K/N][IR] Turned off partial tail call optimization
It is difficult to implement properly tail calls for a real coroutine (when there are other non tail calls), because of continuation interception semantics. And the benefits aren't clear at this point, so let's turn it off for now.
This commit is contained in:
+5
-6
@@ -33,7 +33,6 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
||||
stateMachineFunction: IrFunction,
|
||||
transformingFunction: IrFunction,
|
||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>,
|
||||
tailSuspendCalls: Set<IrCall>
|
||||
)
|
||||
|
||||
protected abstract fun IrBlockBodyBuilder.generateCoroutineStart(invokeSuspendFunction: IrFunction, receiver: IrExpression)
|
||||
@@ -64,7 +63,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
||||
|
||||
val (tailSuspendCalls, hasNotTailSuspendCalls) = collectTailSuspendCalls(context, function)
|
||||
return if (hasNotTailSuspendCalls) {
|
||||
listOf<IrDeclaration>(buildCoroutine(function, tailSuspendCalls).clazz, function)
|
||||
listOf<IrDeclaration>(buildCoroutine(function).clazz, function)
|
||||
} else {
|
||||
// Otherwise, no suspend calls at all or all of them are tail calls - no need in a state machine.
|
||||
// Have to simplify them though (convert them to proper return statements).
|
||||
@@ -101,8 +100,8 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
||||
private val getContinuationSymbol = symbols.getContinuation
|
||||
private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol
|
||||
|
||||
private fun buildCoroutine(irFunction: IrSimpleFunction, tailSuspendCalls: Set<IrCall>) =
|
||||
CoroutineBuilder(irFunction, tailSuspendCalls).build().also { coroutine ->
|
||||
private fun buildCoroutine(irFunction: IrSimpleFunction) =
|
||||
CoroutineBuilder(irFunction).build().also { coroutine ->
|
||||
// Replace original function with a call to constructor of the built coroutine.
|
||||
val irBuilder = context.createIrBuilder(irFunction.symbol, irFunction.startOffset, irFunction.endOffset)
|
||||
irFunction.body = irBuilder.irBlockBody(irFunction) {
|
||||
@@ -129,7 +128,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
||||
|
||||
private class BuiltCoroutine(val clazz: IrClass, val constructor: IrConstructor, val stateMachineFunction: IrFunction)
|
||||
|
||||
private inner class CoroutineBuilder(val irFunction: IrFunction, val tailSuspendCalls: Set<IrCall>) {
|
||||
private inner class CoroutineBuilder(val irFunction: IrFunction) {
|
||||
private val functionParameters = irFunction.explicitParameters
|
||||
|
||||
private val coroutineClass: IrClass =
|
||||
@@ -250,7 +249,7 @@ abstract class AbstractSuspendFunctionsLowering<C : CommonBackendContext>(val co
|
||||
overriddenSymbols += stateMachineFunction.symbol
|
||||
}
|
||||
|
||||
buildStateMachine(function, irFunction, argumentToPropertiesMap, tailSuspendCalls)
|
||||
buildStateMachine(function, irFunction, argumentToPropertiesMap)
|
||||
return function
|
||||
}
|
||||
}
|
||||
|
||||
-2
@@ -355,8 +355,6 @@ internal abstract class KonanSymbols(
|
||||
val invokeSuspendFunction =
|
||||
baseContinuationImpl.findMemberSimpleFunction(Name.identifier("invokeSuspend"))!!
|
||||
|
||||
val completionGetter = baseContinuationImpl.findMemberPropertyGetter(Name.identifier("completion"))!!
|
||||
|
||||
override val coroutineSuspendedGetter = symbolTable.referenceSimpleFunction(
|
||||
coroutinesIntrinsicsPackage
|
||||
.getContributedVariables(COROUTINE_SUSPENDED_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
|
||||
+5
-74
@@ -2,7 +2,6 @@ package org.jetbrains.kotlin.backend.konan.lower
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName
|
||||
import org.jetbrains.kotlin.backend.common.lower.*
|
||||
import org.jetbrains.kotlin.backend.common.lower.coroutines.getOrCreateFunctionWithContinuationStub
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.NativeGenerationState
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
@@ -62,13 +61,11 @@ internal class NativeSuspendFunctionsLowering(
|
||||
)
|
||||
}
|
||||
|
||||
private val getContinuation = context.ir.symbols.getContinuation
|
||||
private val completionGetter = context.ir.symbols.completionGetter
|
||||
|
||||
override fun buildStateMachine(stateMachineFunction: IrFunction,
|
||||
transformingFunction: IrFunction,
|
||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>,
|
||||
tailSuspendCalls: Set<IrCall>) {
|
||||
override fun buildStateMachine(
|
||||
stateMachineFunction: IrFunction,
|
||||
transformingFunction: IrFunction,
|
||||
argumentToPropertiesMap: Map<IrValueParameter, IrField>,
|
||||
) {
|
||||
val originalBody = transformingFunction.body!!
|
||||
val resultArgument = stateMachineFunction.valueParameters.single()
|
||||
|
||||
@@ -83,72 +80,6 @@ internal class NativeSuspendFunctionsLowering(
|
||||
|
||||
val irBuilder = context.createIrBuilder(stateMachineFunction.symbol, startOffset, endOffset)
|
||||
stateMachineFunction.body = irBuilder.irBlockBody(startOffset, endOffset) {
|
||||
if (tailSuspendCalls.isNotEmpty()) {
|
||||
/*
|
||||
* Usual suspend call of, say, function foo will be transformed to something like this:
|
||||
* val result = foo(.., continuation = this)
|
||||
* if (result == COROUTINE_SUSPENDED) return COROUTINE_SUSPENDED
|
||||
* if (result.failed)
|
||||
* this.completion.resumeWithException(result.exception)
|
||||
* else this.completion.resumeWith(result.value)
|
||||
*
|
||||
* But, if a call to foo is a tail call, we can replace the above with just:
|
||||
* return foo(.., continuation = this.completion)
|
||||
*
|
||||
* The visitor below does exactly this.
|
||||
*/
|
||||
originalBody.transformChildren(object : IrElementTransformer<Boolean> {
|
||||
fun IrBuilderWithScope.irGetCompletion() = irCall(completionGetter).apply { dispatchReceiver = irGet(thisReceiver) }
|
||||
|
||||
override fun visitCall(expression: IrCall, /* substituteContinuation */ data: Boolean): IrExpression {
|
||||
val isTailSuspendCall = expression in tailSuspendCalls
|
||||
|
||||
return when {
|
||||
expression.symbol == getContinuation -> {
|
||||
if (data)
|
||||
irBuilder.at(expression).irGetCompletion()
|
||||
else
|
||||
expression
|
||||
}
|
||||
|
||||
expression.isReturnIfSuspendedCall() -> {
|
||||
if (!isTailSuspendCall)
|
||||
expression
|
||||
else {
|
||||
expression.transformChildren(this, /* substituteContinuation = */ true)
|
||||
|
||||
irBuilder.at(expression).irReturn(expression.getValueArgument(0)!!)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
// Only the top-most call should have its continuation substituted.
|
||||
val substituteContinuation = data && !expression.isSuspendCall
|
||||
expression.transformChildren(this, substituteContinuation)
|
||||
|
||||
if (!isTailSuspendCall)
|
||||
expression
|
||||
else {
|
||||
val oldFun = expression.symbol.owner
|
||||
val newFun: IrSimpleFunction = oldFun.getOrCreateFunctionWithContinuationStub(this@NativeSuspendFunctionsLowering.context)
|
||||
|
||||
irBuilder.at(expression).irReturn(
|
||||
irCall(
|
||||
expression,
|
||||
newFun.symbol,
|
||||
newReturnType = newFun.returnType,
|
||||
newSuperQualifierSymbol = expression.superQualifierSymbol
|
||||
).also {
|
||||
it.putValueArgument(it.valueArgumentsCount - 1, irGetCompletion())
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}, /* substituteContinuation = */ false)
|
||||
}
|
||||
|
||||
val suspendResult = irVar("suspendResult".synthesizedName, context.irBuiltIns.anyNType, true)
|
||||
|
||||
// Extract all suspend calls to temporaries in order to make correct jumps to them.
|
||||
|
||||
Reference in New Issue
Block a user