From 379390c472056654fb43ee8d356545531542925b Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Fri, 26 Apr 2019 16:21:03 +0300 Subject: [PATCH] Introduce add continuation pass, which transforms each suspend function and call to have additional continuation parameter. --- .../common/descriptors/WrappedDescriptors.kt | 16 +- .../kotlin/backend/jvm/JvmBackendContext.kt | 15 +- .../jetbrains/kotlin/backend/jvm/JvmLower.kt | 16 +- .../backend/jvm/codegen/FunctionCodegen.kt | 12 +- .../jvm/lower/AddContinuationLowering.kt | 198 ++++++++++++++++++ .../testData/codegen/box/coroutines/simple.kt | 1 - 6 files changed, 240 insertions(+), 18 deletions(-) create mode 100644 compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt index 3bc993ccfea..183a9237a4d 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/descriptors/WrappedDescriptors.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2019 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. */ @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl import org.jetbrains.kotlin.descriptors.annotations.Annotations import org.jetbrains.kotlin.descriptors.impl.ReceiverParameterDescriptorImpl +import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBasedDeclarationDescriptor @@ -452,7 +453,18 @@ open class WrappedSimpleFunctionDescriptor( override fun getUserData(key: CallableDescriptor.UserDataKey?): V? = null override fun newCopyBuilder(): FunctionDescriptor.CopyBuilder { - TODO("not implemented") + return SimpleFunctionDescriptorImpl + .create(containingDeclaration, annotations, name, kind, source) + .initialize( + extensionReceiverParameter, + dispatchReceiverParameter, + typeParameters, + valueParameters, + returnType, + modality, + visibility, + null + ).newCopyBuilder() } override fun accept(visitor: DeclarationDescriptorVisitor?, data: D) = diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt index 7116767760f..5f2780f1b59 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmBackendContext.kt @@ -1,5 +1,5 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2019 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. */ @@ -12,16 +12,19 @@ import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods import org.jetbrains.kotlin.codegen.state.GenerationState +import org.jetbrains.kotlin.config.coroutinesPackageFqName import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.util.ReferenceSymbolTable import org.jetbrains.kotlin.ir.util.SymbolTable import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.psi2ir.PsiSourceManager class JvmBackendContext( @@ -48,6 +51,16 @@ class JvmBackendContext( override val internalPackageFqn = FqName("kotlin.jvm") + val transformedSuspendFunctionsCache = mutableMapOf() + + private val coroutinePackage = state.module.getPackage(state.languageVersionSettings.coroutinesPackageFqName()) + + val continuationClass = symbolTable.referenceClass( + coroutinePackage.memberScope.getContributedClassifier( + Name.identifier("Continuation"), NoLookupLocation.FROM_BACKEND + ) as ClassDescriptor + ) + internal fun getTopLevelClass(fqName: FqName): IrClassSymbol { val descriptor = state.module.getPackage(fqName.parent()).memberScope.getContributedClassifier( fqName.shortName(), NoLookupLocation.FROM_BACKEND diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt index 47baf8f6fd4..84975e1baaf 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmLower.kt @@ -1,17 +1,6 @@ /* - * Copyright 2010-2017 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright 2010-2019 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.jvm @@ -129,6 +118,7 @@ val jvmPhases = namedIrFilePhase( singletonReferencesPhase then localDeclarationsPhase then singleAbstractMethodPhase then + addContinuationPhase then callableReferencePhase then functionNVarargInvokePhase then diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt index 4f865feec1c..10256673f94 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/FunctionCodegen.kt @@ -1,18 +1,27 @@ /* - * Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Copyright 2010-2019 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.jvm.codegen +import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.builtins.KotlinBuiltIns.FQ_NAMES import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.state.GenerationState import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.createType +import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.annotations.JVM_SYNTHETIC_ANNOTATION_FQ_NAME import org.jetbrains.kotlin.resolve.jvm.annotations.STRICTFP_ANNOTATION_FQ_NAME @@ -20,6 +29,7 @@ import org.jetbrains.kotlin.resolve.jvm.annotations.SYNCHRONIZED_ANNOTATION_FQ_N import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodGenericSignature import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodParameterKind import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature +import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.Opcodes diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt new file mode 100644 index 00000000000..fee2a16dcac --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/AddContinuationLowering.kt @@ -0,0 +1,198 @@ +/* + * Copyright 2010-2019 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.jvm.lower + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.descriptors.WrappedValueParameterDescriptor +import org.jetbrains.kotlin.backend.common.ir.copyTo +import org.jetbrains.kotlin.backend.common.ir.copyTypeParametersFrom +import org.jetbrains.kotlin.backend.common.ir.isSuspend +import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.codegen.coroutines.getOrCreateJvmSuspendFunctionView +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrValueParameterImpl +import org.jetbrains.kotlin.ir.expressions.* +import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl +import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl +import org.jetbrains.kotlin.ir.types.IrType +import org.jetbrains.kotlin.ir.types.createType +import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.ir.visitors.* +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.types.Variance +import java.util.* + +// TODO: Move to common prefix +internal val addContinuationPhase = makeIrFilePhase( + ::AddContinuationLowering, + "AddContinuation", + "Add continuation parameter to suspend functions and suspend calls" +) + +private class AddContinuationLowering(private val context: JvmBackendContext) : FileLoweringPass { + object CONTINUATION_PARAMETER : IrDeclarationOriginImpl("CONTINUATION_PARAMETER") + + override fun lower(irFile: IrFile) { + val suspendLambdas = markSuspendLambdas(irFile) + addContinuationParameter(irFile) + transformSuspendCalls(irFile, suspendLambdas) + } + + private fun addContinuationParameter(irFile: IrFile) { + fun tryAddContinuationParameter(element: IrElement): List? { + return if (element is IrSimpleFunction && element.isSuspend) { + listOf(element.getOrCreateView()) + } else null + } + + irFile.transformDeclarationsFlat(::tryAddContinuationParameter) + irFile.acceptVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitClass(declaration: IrClass) { + declaration.acceptChildrenVoid(this) + declaration.transformDeclarationsFlat(::tryAddContinuationParameter) + } + }) + } + + private fun markSuspendLambdas(irElement: IrElement): List { + val suspendLambdas = arrayListOf() + irElement.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitFunctionReference(expression: IrFunctionReference) { + expression.acceptChildrenVoid(this) + + if (expression.isSuspend) { + suspendLambdas += expression.symbol.owner + } + } + }) + return suspendLambdas + } + + private fun transformSuspendCalls(irFile: IrFile, suspendLambdas: List) { + irFile.transformChildrenVoid(object : IrElementTransformerVoid() { + private val functionStack = Stack() + + override fun visitElement(element: IrElement): IrElement { + element.transformChildrenVoid(this) + return element + } + + override fun visitFunction(declaration: IrFunction): IrStatement { + functionStack.push(declaration) + val res = super.visitFunction(declaration) + functionStack.pop() + return res + } + + override fun visitCall(expression: IrCall): IrExpression { + if (!expression.isSuspend) return super.visitCall(expression) + val caller = functionStack.peek() + val continuationArgument = IrGetValueImpl( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + continuationType(caller.returnType), + if (caller in suspendLambdas) (caller.dispatchReceiverParameter as IrValueParameter).symbol + else caller.valueParameters.last().symbol + ) + val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView() + val res = IrCallImpl(expression.startOffset, expression.endOffset, expression.type, view.symbol).apply { + copyTypeArgumentsFrom(expression) + dispatchReceiver = expression.dispatchReceiver + for (i in 0 until expression.valueArgumentsCount) { + putValueArgument(i, expression.getValueArgument(i)) + } + putValueArgument(expression.valueArgumentsCount, continuationArgument) + } + return super.visitCall(res) + } + + override fun visitFunctionReference(expression: IrFunctionReference): IrExpression { + if (!expression.isSuspend) return super.visitFunctionReference(expression) + val view = (expression.symbol.owner as IrSimpleFunction).getOrCreateView() + val res = IrFunctionReferenceImpl( + expression.startOffset, + expression.endOffset, + expression.type, + view.symbol, + view.descriptor, + expression.typeArgumentsCount, + expression.origin + ).apply { + copyTypeArgumentsFrom(expression) + dispatchReceiver = expression.dispatchReceiver + for (i in 0 until expression.valueArgumentsCount) { + putValueArgument(i, expression.getValueArgument(i)) + } + } + return super.visitFunctionReference(res) + } + }) + } + + private fun IrSimpleFunction.getOrCreateView(): IrSimpleFunction = + context.transformedSuspendFunctionsCache.getOrPut(this) { + IrFunctionImpl( + startOffset, endOffset, origin, getOrCreateJvmSuspendFunctionView(descriptor, context.state), returnType + ).also { + it.parent = parent + it.copyTypeParametersFrom(this) + + it.dispatchReceiverParameter = dispatchReceiverParameter?.copyTo(it) + + valueParameters.mapTo(it.valueParameters) { p -> p.copyTo(it) } + it.valueParameters += createContinuationValueParameter(it.valueParameters.size, returnType).apply { parent = it } + + // Fix links to parameters + val valueParametersMapping = (valueParameters + dispatchReceiverParameter) + .zip(it.valueParameters.dropLast(1) + it.dispatchReceiverParameter).toMap() + it.body = body?.deepCopyWithSymbols(this) + it.body?.transformChildrenVoid(object : IrElementTransformerVoid() { + override fun visitGetValue(expression: IrGetValue) = + valueParametersMapping[expression.symbol.owner]?.let { newParam -> + expression.run { IrGetValueImpl(startOffset, endOffset, type, newParam.symbol, origin) } + } ?: expression + }) + } + } + + private fun createContinuationValueParameter(index: Int, returnType: IrType): IrValueParameter { + val descriptor = WrappedValueParameterDescriptor() + return IrValueParameterImpl( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + CONTINUATION_PARAMETER, + IrValueParameterSymbolImpl(descriptor), + Name.identifier("\$completion"), + index, + continuationType(returnType), + null, isCrossinline = false, isNoinline = false + ).also { + descriptor.bind(it) + } + } + + private fun continuationType(returnType: IrType) = context.continuationClass.createType( + hasQuestionMark = false, + arguments = listOf(makeTypeProjection(returnType, Variance.INVARIANT)) + ) + +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/coroutines/simple.kt b/compiler/testData/codegen/box/coroutines/simple.kt index 294e7760e8f..3731dd4ec0b 100644 --- a/compiler/testData/codegen/box/coroutines/simple.kt +++ b/compiler/testData/codegen/box/coroutines/simple.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST