From 817dba8564893782b16a6d85afeef2752a38d3ff Mon Sep 17 00:00:00 2001 From: Pavel Kunyavskiy Date: Mon, 5 Sep 2022 16:14:29 +0200 Subject: [PATCH] [K/N] Use suspend function desugaring lowerings. --- .../kotlin/backend/common/Mappings.kt | 2 + .../lower/AbstractSuspendFunctionsLowering.kt | 112 +---------------- .../lower/SingleAbstractMethodLowering.kt | 2 +- .../AddContinuationToFunctionsLowering.kt | 8 +- .../backend/konan/KonanLoweringPhases.kt | 10 +- .../konan/descriptors/ClassLayoutBuilder.kt | 10 +- .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 2 - .../backend/konan/llvm/IntrinsicGenerator.kt | 15 +-- ...ctionSupertypeToSuspendFunctionLowering.kt | 119 ++++++++++++++++++ .../konan/lower/FunctionReferenceLowering.kt | 19 +-- ...eAddContinuationToFunctionCallsLowering.kt | 30 +++++ .../NativeSingleAbstractMethodLowering.kt | 5 + .../lower/NativeSuspendFunctionLowering.kt | 13 -- .../backend/konan/optimizations/DFGBuilder.kt | 5 - .../FileInitializersOptimization.kt | 3 - .../unreachableStatementAfterReturn.kt | 33 +++++ .../unreachableStatementAfterReturn.out | 4 + .../kotlin/native/internal/Coroutines.kt | 3 - .../kotlin/native/internal/IntrinsicType.kt | 1 - 19 files changed, 227 insertions(+), 169 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/AddFunctionSupertypeToSuspendFunctionLowering.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt index 0d6794e9374..4c6051d248a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/Mappings.kt @@ -20,6 +20,7 @@ interface Mapping { val capturedConstructors: Delegate val reflectedNameAccessor: Delegate val suspendFunctionsToFunctionWithContinuations: Delegate + val functionWithContinuationsToSuspendFunctions: Delegate abstract class Delegate { abstract operator fun get(key: K): V? @@ -77,6 +78,7 @@ open class DefaultMapping(delegateFactory: DelegateFactory = DefaultDelegateFact override val capturedConstructors: Mapping.Delegate = delegateFactory.newDeclarationToDeclarationMapping() override val reflectedNameAccessor: Mapping.Delegate = delegateFactory.newDeclarationToDeclarationMapping() override val suspendFunctionsToFunctionWithContinuations: Mapping.Delegate = delegateFactory.newDeclarationToDeclarationMapping() + override val functionWithContinuationsToSuspendFunctions: Mapping.Delegate = delegateFactory.newDeclarationToDeclarationMapping() } fun KMutableProperty0.getOrPut(fn: () -> V) = this.get() ?: fn().also { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt index 5c9f6e8b246..c64199b569a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/AbstractSuspendFunctionsLowering.kt @@ -6,8 +6,6 @@ package org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.common.* -import org.jetbrains.kotlin.backend.common.descriptors.synthesizedName -import org.jetbrains.kotlin.backend.common.ir.* import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrElement @@ -19,7 +17,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrInstanceInitializerCallImpl import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.types.impl.IrSimpleTypeImpl import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.name.Name @@ -33,9 +30,6 @@ abstract class AbstractSuspendFunctionsLowering(val co protected abstract val stateMachineMethodName: Name protected abstract fun getCoroutineBaseClass(function: IrFunction): IrClassSymbol protected abstract fun nameForCoroutineClass(function: IrFunction): Name - protected abstract fun IrBuilderWithScope.launchSuspendFunctionWithGivenContinuation( - symbol: IrSimpleFunctionSymbol, superQualifierSymbol: IrClassSymbol?, dispatchReceiver: IrExpression, - arguments: List, continuation: IrExpression) : IrExpression protected abstract fun buildStateMachine( stateMachineFunction: IrFunction, @@ -57,106 +51,6 @@ abstract class AbstractSuspendFunctionsLowering(val co markSuspendLambdas(irFile) buildCoroutines(irFile) transformCallableReferencesToSuspendLambdas(irFile) - addMissingSupertypesToSuspendFunctionImplementingClasses(irFile) - } - - private fun addMissingSupertypesToSuspendFunctionImplementingClasses(irFile: IrFile) { - irFile.acceptChildrenVoid(object : IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - // Don't need to iterate through children. All local classes are already moved to the top level by this moment. - } - - override fun visitClass(declaration: IrClass) { - addMissingSupertypes(declaration) - declaration.acceptChildrenVoid(this) - } - - - private fun addMissingSupertypes(clazz: IrClass) { - val suspendFunctionTypes = getAllSubstitutedSupertypes(clazz).filter { - // SuspendFunction class is some hack in old Kotlin/Native compiler versions. - // It's not used now, but is considered as SuspendFunction-like class in isSuspendFunction util, - // if found in old klib. We need just to ignore it. - it.isSuspendFunction() && it.classOrNull?.owner?.name?.toString() != "SuspendFunction" - }.toSet() - - for (suspendFunctionType in suspendFunctionTypes) { - val suspendFunctionClassSymbol = suspendFunctionType.classOrNull ?: continue - val suspendFunction = suspendFunctionClassSymbol.owner.simpleFunctions().single { - it.name == OperatorNameConventions.INVOKE - } - - val invokeFunction = clazz.simpleFunctions().single { - it.name == OperatorNameConventions.INVOKE && it.overrides(suspendFunction) - } - - if (invokeFunction.modality == Modality.ABSTRACT) { - continue - } - - val suspendFunctionArity = suspendFunction.valueParameters.size - val functionClassSymbol = symbols.functionN(suspendFunctionArity + 1) - val functionSymbol = functionClassSymbol.owner.simpleFunctions().single { - it.name == OperatorNameConventions.INVOKE - }.symbol - - val functionClassTypeArguments = suspendFunctionType.arguments.mapIndexed { index, argument -> - val type = (argument as IrTypeProjection).type - if (index == suspendFunctionArity) continuationClassSymbol.typeWith(type) else type - } + context.irBuiltIns.anyNType - - val functionType = functionClassSymbol.typeWith(functionClassTypeArguments) - - clazz.superTypes += functionType - - context.irFactory.buildFun { - startOffset = SYNTHETIC_OFFSET - endOffset = SYNTHETIC_OFFSET - origin = DECLARATION_ORIGIN_COROUTINE_IMPL - name = OperatorNameConventions.INVOKE - visibility = DescriptorVisibilities.PROTECTED - returnType = context.irBuiltIns.anyNType - }.apply { - parent = clazz - clazz.declarations += this - - typeParameters = invokeFunction.typeParameters.map { parameter -> - parameter.copyToWithoutSuperTypes(this, origin = DECLARATION_ORIGIN_COROUTINE_IMPL) - .apply { superTypes += parameter.superTypes } - } - - valueParameters = invokeFunction.valueParameters - .mapIndexed { index, parameter -> - parameter.copyTo(this, DECLARATION_ORIGIN_COROUTINE_IMPL, index) - } - valueParameters += buildValueParameter(this) { - index = valueParameters.size - type = context.irBuiltIns.anyNType - name = "completion".synthesizedName - } - - this.createDispatchReceiverParameter() - - overriddenSymbols += functionSymbol - - val thisReceiver = dispatchReceiverParameter!! - - val irBuilder = context.createIrBuilder(symbol, startOffset, endOffset) - body = irBuilder.irBlockBody(startOffset, endOffset) { - +irReturn( - launchSuspendFunctionWithGivenContinuation( - invokeFunction.symbol, - superQualifierSymbol = invokeFunction.parentAsClass.symbol, // Call non-virtually. - irGet(thisReceiver), - valueParameters.dropLast(1).map { irGet(it) }, - irGet(valueParameters.last()) - ) - ) - } - } - } - } - }) } private fun buildCoroutines(irFile: IrFile) { @@ -433,7 +327,9 @@ abstract class AbstractSuspendFunctionsLowering(val co ) val suspendInvokeFunction = - suspendFunctionClass!!.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE } + suspendFunctionClass!!.simpleFunctions().single { it.name == OperatorNameConventions.INVOKE }.let { + context.mapping.functionWithContinuationsToSuspendFunctions[it] ?: it + } buildInvokeMethod( functionInvokeFunction = suspendInvokeFunction, @@ -722,3 +618,5 @@ abstract class AbstractSuspendFunctionsLowering(val co private val CREATE_IDENTIFIER = Name.identifier("create") } } + + diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt index 335e811646b..365f4713229 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/SingleAbstractMethodLowering.kt @@ -226,7 +226,7 @@ abstract class SingleAbstractMethodLowering(val context: CommonBackendContext) : body = context.createIrBuilder(symbol).irBlockBody { +irReturn( irCall( - wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }.symbol, + getSuspendFunctionWithoutContinuation(wrappedFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE }).symbol, originalSuperMethod.returnType ).apply { dispatchReceiver = irGetField(irGet(dispatchReceiverParameter!!), field) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionsLowering.kt index 57265470295..6d24523a1a4 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/coroutines/AddContinuationToFunctionsLowering.kt @@ -88,12 +88,14 @@ private fun transformSuspendFunction(context: CommonBackendContext, function: Ir fun IrSimpleFunction.getOrCreateFunctionWithContinuationStub(context: CommonBackendContext): IrSimpleFunction { return context.mapping.suspendFunctionsToFunctionWithContinuations.getOrPut(this) { - createSuspendFunctionStub(context) + createSuspendFunctionStub(context).also { + context.mapping.functionWithContinuationsToSuspendFunctions[it] = this + } } } private fun IrSimpleFunction.createSuspendFunctionStub(context: CommonBackendContext): IrSimpleFunction { - require(this.isSuspend) + require(this.isSuspend) { "$fqNameWhenAvailable should be a suspend function to create version with contunation" } return factory.buildFun { updateFrom(this@createSuspendFunctionStub) isSuspend = false @@ -103,9 +105,9 @@ private fun IrSimpleFunction.createSuspendFunctionStub(context: CommonBackendCon }.also { function -> function.parent = parent - function.annotations += annotations function.metadata = metadata + function.copyAnnotationsFrom(this) function.copyAttributes(this) function.copyTypeParametersFrom(this) val substitutionMap = makeTypeParameterSubstitutionMap(this, function) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index 918a65b5428..cf74192deae 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -2,6 +2,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.common.* import org.jetbrains.kotlin.backend.common.lower.* +import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering @@ -383,8 +384,13 @@ internal val varargPhase = makeKonanFileLoweringPhase( prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck) ) -internal val coroutinesPhase = makeKonanFileLoweringPhase( - ::NativeSuspendFunctionsLowering, +internal val coroutinesPhase = makeKonanFileOpPhase( + { context, irFile -> + NativeSuspendFunctionsLowering(context).lower(irFile) + AddContinuationToNonLocalSuspendFunctionsLowering(context).lower(irFile) + NativeAddContinuationToFunctionCallsLowering(context).lower(irFile) + AddFunctionSupertypeToSuspendFunctionLowering(context).lower(irFile) + }, name = "Coroutines", description = "Coroutines lowering", prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index e8576204b2b..a5a8053901a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -356,6 +356,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { val interfaceVTableEntries: List by lazy { require(irClass.isInterface) irClass.simpleFunctions() + .map { it.getLoweredVersion() } .filter { f -> f.isOverridable && f.bridgeTarget == null && (f.isReal || f.overriddenSymbols.any { f.needBridgeTo(it.owner) }) @@ -503,8 +504,15 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { } } + /** + * Normally, function should be already replaced. But if the function come from LazyIr, it can be not replaced. + */ + fun IrSimpleFunction.getLoweredVersion() = context.mapping.suspendFunctionsToFunctionWithContinuations[this] ?: this + private val overridableOrOverridingMethods: List - get() = irClass.simpleFunctions().filter { it.isOverridableOrOverrides && it.bridgeTarget == null } + get() = irClass.simpleFunctions() + .map {it.getLoweredVersion() } + .filter { it.isOverridableOrOverrides && it.bridgeTarget == null } private val IrFunction.uniqueName get() = computeFunctionName() } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 764ad775e4c..33953c3304c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -324,8 +324,6 @@ internal class KonanSymbols( override val returnIfSuspended = internalFunction("returnIfSuspended") - val coroutineLaunchpad = internalFunction("coroutineLaunchpad") - override val suspendCoroutineUninterceptedOrReturn = internalFunction("suspendCoroutineUninterceptedOrReturn") private val coroutinesIntrinsicsPackage = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt index 7844ed88ac4..8aa0cfa685a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IntrinsicGenerator.kt @@ -72,7 +72,6 @@ internal enum class IntrinsicType { // Coroutines GET_CONTINUATION, RETURN_IF_SUSPENDED, - COROUTINE_LAUNCHPAD, // Interop INTEROP_READ_BITS, INTEROP_WRITE_BITS, @@ -160,6 +159,7 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv * processes it. Otherwise it returns null. */ fun tryEvaluateSpecialCall(callSite: IrFunctionAccessExpression, resultSlot: LLVMValueRef?): LLVMValueRef? { + resultSlot.let{} val function = callSite.symbol.owner if (!function.isTypedIntrinsic) { return null @@ -184,18 +184,6 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv ) codegen.theUnitInstanceRef.llvm } - IntrinsicType.COROUTINE_LAUNCHPAD -> { - val suspendFunctionCall = callSite.getValueArgument(0) as IrCall - val continuation = environment.evaluateExpression(callSite.getValueArgument(1)!!, null) - val suspendFunction = suspendFunctionCall.symbol.owner - assert(suspendFunction.isSuspend) { "Call to a suspend function expected but was ${suspendFunction.dump()}" } - environment.evaluateCall(suspendFunction, - environment.evaluateExplicitArgs(suspendFunctionCall) + listOf(continuation), - environment.calculateLifetime(suspendFunctionCall), - suspendFunctionCall.superQualifierSymbol?.owner, - resultSlot, - ) - } else -> null } } @@ -270,7 +258,6 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv reportNonLoweredIntrinsic(intrinsicType) IntrinsicType.INIT_INSTANCE, IntrinsicType.OBJC_INIT_BY, - IntrinsicType.COROUTINE_LAUNCHPAD, IntrinsicType.OBJC_GET_SELECTOR, IntrinsicType.IMMUTABLE_BLOB -> reportSpecialIntrinsic(intrinsicType) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/AddFunctionSupertypeToSuspendFunctionLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/AddFunctionSupertypeToSuspendFunctionLowering.kt new file mode 100644 index 00000000000..583c6c9aca4 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/AddFunctionSupertypeToSuspendFunctionLowering.kt @@ -0,0 +1,119 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.coroutines.getOrCreateFunctionWithContinuationStub +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrClass +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid +import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid +import org.jetbrains.kotlin.util.OperatorNameConventions + +/** + * This lowering is a hack, to provide compatibility with leaked jvm implementation detail. + * + * On jvm SuspendFunctionN is implicitly implementing FunctionN+1, + * and visa versa, and it can be used for reusing continuation objects for performance improvements. + * + * Also, it is used in startCoroutineUninterceptedOrReturn intrinsic. + * + * So we are adding corresponding FunctionN+1 into supper types. + * At the current point, lowered suspend function signature is overriding its invoke method. + * + */ +internal class AddFunctionSupertypeToSuspendFunctionLowering(val context: Context) : FileLoweringPass { + override fun lower(irFile: IrFile) { + irFile.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + // Don't need to iterate through children. All local classes are already moved to the top level by this moment. + } + + override fun visitClass(declaration: IrClass) { + addMissingSupertypes(declaration) + declaration.acceptChildrenVoid(this) + } + + private fun IrSimpleFunction.getLowered() = if (isSuspend) + getOrCreateFunctionWithContinuationStub(context) + else + this + + private fun IrClass.getInvokeFunction() = simpleFunctions().single { + it.name == OperatorNameConventions.INVOKE + }.getLowered() + + private fun addOverride(clazz: IrClass, alreadyOverridden: IrType, toOverride: IrType) { + val alreadyOverriddenFunction = alreadyOverridden.classOrNull!!.owner.getInvokeFunction() + val functionToOverride = toOverride.classOrNull!!.owner.getInvokeFunction() + val invokeFunction = clazz.simpleFunctions().single { it.overrides(alreadyOverriddenFunction) } + if (invokeFunction.modality == Modality.ABSTRACT) return + clazz.superTypes += toOverride + invokeFunction.overriddenSymbols += functionToOverride.symbol + } + + + private fun addMissingSupertypes(clazz: IrClass) { + val suspendFunctionSuperTypes = getAllSubstitutedSupertypes(clazz).filter { + // SuspendFunction class is some hack in old Kotlin/Native compiler versions. + // It's not used now, but is considered as SuspendFunction-like class in isSuspendFunction util, + // if found in old klib. We need just to ignore it. + it.isSuspendFunction() && it.classOrNull?.owner?.name?.toString() != "SuspendFunction" + }.toSet() + + val continuationClassSymbol = context.ir.symbols.continuationClass + + fun IrSimpleType.getClassAt(index: Int) = (this.arguments.getOrNull(index) as? IrTypeProjection)?.type?.classOrNull + + val functionWithContinuationSuperTypes = getAllSubstitutedSupertypes(clazz).filter { + it.isFunction() && + it.getClassAt(it.arguments.size - 2) == continuationClassSymbol + }.toSet() + + for (suspendFunctionType in suspendFunctionSuperTypes) { + val functionClassTypeArguments = suspendFunctionType.arguments.mapIndexed { index, argument -> + val type = (argument as IrTypeProjection).type + if (index == suspendFunctionType.arguments.indices.last) { + continuationClassSymbol.typeWith(type) + } else { + type + } + } + context.irBuiltIns.anyNType + + val functionType = context.ir.symbols.functionN(functionClassTypeArguments.size - 1).typeWith(functionClassTypeArguments) + + addOverride(clazz, suspendFunctionType, functionType) + } + + for (functionType in functionWithContinuationSuperTypes) { + val suspendFunctionClassTypeArguments = functionType.arguments.dropLast(1).mapIndexed { index, argument -> + val type = (argument as IrTypeProjection).type + if (index == functionType.arguments.indices.last - 1) { + require(type.classOrNull == continuationClassSymbol) + when (val typeArgument = (type as IrSimpleType).arguments.single()) { + is IrTypeProjection -> typeArgument.type + else -> context.irBuiltIns.anyNType + } + } else { + type + } + } + + val suspendFunctionType = context.ir.symbols.suspendFunctionN(suspendFunctionClassTypeArguments.size - 1).typeWith(suspendFunctionClassTypeArguments) + addOverride(clazz, functionType, suspendFunctionType) + } + } + + }) + } + +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt index 1f4223750a3..5ffcaf6c9f7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/FunctionReferenceLowering.kt @@ -171,8 +171,6 @@ internal class FunctionReferenceLowering(val context: Context) : FileLoweringPas private val irBuiltIns = context.irBuiltIns private val symbols = context.ir.symbols - private val getContinuationSymbol = symbols.getContinuation - private val continuationClassSymbol = getContinuationSymbol.owner.returnType.classifierOrFail as IrClassSymbol private val startOffset = functionReference.startOffset private val endOffset = functionReference.endOffset private val referencedFunction = functionReference.symbol.owner @@ -268,18 +266,8 @@ internal class FunctionReferenceLowering(val context: Context) : FileLoweringPas superTypes += suspendFunctionClass.typeWith(functionParameterAndReturnTypes) } else { functionClass = (if (isKFunction) symbols.kFunctionN(numberOfParameters) else symbols.functionN(numberOfParameters)).owner + suspendFunctionClass = null superTypes += functionClass.typeWith(functionParameterAndReturnTypes) - val lastParameterType = unboundFunctionParameters.lastOrNull()?.type - if (lastParameterType?.classifierOrNull != continuationClassSymbol) - suspendFunctionClass = null - else { - lastParameterType as IrSimpleType - // If the last parameter is Continuation<> inherit from SuspendFunction. - suspendFunctionClass = symbols.suspendFunctionN(numberOfParameters - 1).owner - val suspendFunctionClassTypeParameters = functionParameterTypes.dropLast(1) + - (lastParameterType.arguments.single().typeOrNull ?: irBuiltIns.anyNType) - superTypes += suspendFunctionClass.symbol.typeWith(suspendFunctionClassTypeParameters) - } } if (functionClass != null) @@ -440,7 +428,10 @@ internal class FunctionReferenceLowering(val context: Context) : FileLoweringPas return false } - private fun buildInvokeMethod(superFunction: IrSimpleFunction): IrSimpleFunction { + private fun buildInvokeMethod(superFunction: IrSimpleFunction) = + buildInvokeMethodImpl(context.mapping.functionWithContinuationsToSuspendFunctions[superFunction] ?: superFunction) + + private fun buildInvokeMethodImpl(superFunction: IrSimpleFunction): IrSimpleFunction { return IrFunctionImpl( startOffset, endOffset, DECLARATION_ORIGIN_FUNCTION_REFERENCE_IMPL, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt new file mode 100644 index 00000000000..9da50eb6eb3 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeAddContinuationToFunctionCallsLowering.kt @@ -0,0 +1,30 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.lower + +import org.jetbrains.kotlin.backend.common.lower.coroutines.AbstractAddContinuationToFunctionCallsLowering +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction +import org.jetbrains.kotlin.ir.util.overrides + +internal class NativeAddContinuationToFunctionCallsLowering(override val context: Context) : AbstractAddContinuationToFunctionCallsLowering() { + /* + * In complex cases suspend functions are converted to state-machine class with invokeSuspend method. + * In that case continuation is an object itself + * In simple cases, function is left as is, and receives continuation as its last parameter + * We should handle both cases here + */ + override fun IrSimpleFunction.getContinuationParameter() = when { + overrides(context.ir.symbols.invokeSuspendFunction.owner) -> dispatchReceiverParameter!! + else -> { + valueParameters.lastOrNull().also { + require(origin == IrDeclarationOrigin.LOWERED_SUSPEND_FUNCTION) { "Continuation parameter only exists in lowered suspend functions, but function origin is $origin" } + require(it != null && it.origin == IrDeclarationOrigin.CONTINUATION) { "Continuation parameter is expected to be last one" } + }!! + } + } +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSingleAbstractMethodLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSingleAbstractMethodLowering.kt index c7566a194eb..52db12c8674 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSingleAbstractMethodLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSingleAbstractMethodLowering.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.ScopeWithIr import org.jetbrains.kotlin.backend.common.lower.SingleAbstractMethodLowering import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.classOrNull @@ -24,4 +25,8 @@ internal class NativeSingleAbstractMethodLowering(context: Context) : SingleAbst } override val IrType.needEqualsHashCodeMethods get() = true + + override fun getSuspendFunctionWithoutContinuation(function: IrSimpleFunction) = function.let { + context.mapping.functionWithContinuationsToSuspendFunctions[it] ?: it + } } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSuspendFunctionLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSuspendFunctionLowering.kt index baa12248383..4c9b354ac3d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSuspendFunctionLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeSuspendFunctionLowering.kt @@ -59,19 +59,6 @@ internal class NativeSuspendFunctionsLowering(ctx: Context): AbstractSuspendFunc } - override fun IrBuilderWithScope.launchSuspendFunctionWithGivenContinuation( - symbol: IrSimpleFunctionSymbol, superQualifierSymbol: IrClassSymbol?, dispatchReceiver: IrExpression, - arguments: List, continuation: IrExpression - ) = irCall(this@NativeSuspendFunctionsLowering.context.ir.symbols.coroutineLaunchpad).apply { - putValueArgument(0, irCall(symbol.owner, superQualifierSymbol = superQualifierSymbol).apply { - this.dispatchReceiver = dispatchReceiver - arguments.forEachIndexed { index, irExpression -> - putValueArgument(index, irExpression) - } - }) - putValueArgument(1, continuation) - } - override fun buildStateMachine(stateMachineFunction: IrFunction, transformingFunction: IrFunction, argumentToPropertiesMap: Map) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index fe483885658..d4b75108dee 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -714,11 +714,6 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag is IrCall -> when (value.symbol) { getContinuationSymbol -> continuationOverride ?: getContinuation().value - symbols.coroutineLaunchpad -> getNode( - value.getValueArgument(0)!!, - continuationOverride = expressionToEdge(value.getValueArgument(1)!!).node - ).value - in arrayGetSymbols -> { val actualCallee = value.actualCallee diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/FileInitializersOptimization.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/FileInitializersOptimization.kt index dd73d0ae154..eaeca337125 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/FileInitializersOptimization.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/FileInitializersOptimization.kt @@ -234,7 +234,6 @@ internal object FileInitializersOptimization { } private val executeImplSymbol = context.ir.symbols.executeImpl - private val coroutineLaunchpadSymbol = context.ir.symbols.coroutineLaunchpad private val getContinuationSymbol = context.ir.symbols.getContinuation private var dummySet = mutableSetOf() @@ -506,8 +505,6 @@ internal object FileInitializersOptimization { return processExecuteImpl(expression, data) if (expression.symbol == getContinuationSymbol) return data - if (expression.symbol == coroutineLaunchpadSymbol) - return processCoroutineLaunchpad(expression, data) if (!expression.isVirtualCall) return processCall(expression, expression.actualCallee, data) val devirtualizedCallSite = virtualCallSites[expression] ?: return data diff --git a/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.kt b/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.kt index b98bf249146..aecc633e216 100644 --- a/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.kt +++ b/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.kt @@ -4,6 +4,7 @@ */ package codegen.function.unreachable_statement_after_return +import kotlin.coroutines.* fun test1(): Any? { return 1 @@ -25,10 +26,42 @@ fun test4(): Int { 42 } +suspend fun test5(): Any? { + return 5 + 42 +} + +suspend fun test6(): Int? { + return 6 + 42 +} + +suspend fun test7(): Any { + return 7 + 42 +} + +suspend fun test8(): Int { + return 8 + 42 +} + +@Suppress("UNCHECKED_CAST") +private fun (suspend () -> T).runCoroutine() : T { + var result : Any? = null + startCoroutine(Continuation(EmptyCoroutineContext) { result = it.getOrThrow() }) + return result as T +} + fun main() { println(test1()) println(test2()) println(test3()) println(test4()) + + println(::test5.runCoroutine()) + println(::test6.runCoroutine()) + println(::test7.runCoroutine()) + println(::test8.runCoroutine()) } diff --git a/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.out b/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.out index 94ebaf90016..535d2b01d33 100644 --- a/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.out +++ b/kotlin-native/backend.native/tests/codegen/function/unreachableStatementAfterReturn.out @@ -2,3 +2,7 @@ 2 3 4 +5 +6 +7 +8 diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Coroutines.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Coroutines.kt index 4ac467ec2cd..c1af7acd4c6 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Coroutines.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/Coroutines.kt @@ -25,6 +25,3 @@ internal inline suspend fun getCoroutineContext(): CoroutineContext = @TypedIntrinsic(IntrinsicType.RETURN_IF_SUSPENDED) @PublishedApi internal external suspend fun returnIfSuspended(@Suppress("UNUSED_PARAMETER") argument: Any?): T - -@TypedIntrinsic(IntrinsicType.COROUTINE_LAUNCHPAD) -internal external fun coroutineLaunchpad(suspendFunctionCall: Any?, continuation: Continuation<*>): Any? \ No newline at end of file diff --git a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt index 64be3c59b71..182d0a8cf99 100644 --- a/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt +++ b/kotlin-native/runtime/src/main/kotlin/kotlin/native/internal/IntrinsicType.kt @@ -61,7 +61,6 @@ class IntrinsicType { // Coroutines const val GET_CONTINUATION = "GET_CONTINUATION" const val RETURN_IF_SUSPENDED = "RETURN_IF_SUSPENDED" - const val COROUTINE_LAUNCHPAD = "COROUTINE_LAUNCHPAD" // Interop const val INTEROP_READ_PRIMITIVE = "INTEROP_READ_PRIMITIVE"