[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:
Igor Chevdar
2023-02-02 20:58:59 +02:00
committed by Space Team
parent 716853dd00
commit c871ccc4dd
3 changed files with 10 additions and 82 deletions
@@ -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
}
}
@@ -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)
@@ -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.