From 01b6ab74a57a2540eb22dbd88beabcb76ee29020 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 25 Apr 2017 17:21:07 +0300 Subject: [PATCH] Codegen: coroutines support --- .../backend/konan/llvm/BinaryInterface.kt | 8 +- .../backend/konan/llvm/CodeGenerator.kt | 12 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 144 ++++++++++++++---- 3 files changed, 135 insertions(+), 29 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt index b2fd656c3cf..81646694b6b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BinaryInterface.kt @@ -195,8 +195,14 @@ private fun getStringValue(annotation: AnnotationDescriptor): String? { // TODO: bring here dependencies of this method? internal fun RuntimeAware.getLlvmFunctionType(function: FunctionDescriptor): LLVMTypeRef { val original = function.original - val returnType = if (original is ConstructorDescriptor) voidType else getLLVMReturnType(original.returnType!!) + val returnType = when { + original is ConstructorDescriptor -> voidType + original.isSuspend -> kObjHeaderPtr // Suspend functions return Any?. + else -> getLLVMReturnType(original.returnType!!) + } val paramTypes = ArrayList(original.allParameters.map { getLLVMType(it.type) }) + if (original.isSuspend) + paramTypes.add(kObjHeaderPtr) // Suspend functions have implicit parameter of type Continuation<>. if (isObjectType(returnType)) paramTypes.add(kObjHeaderPtrPtr) return functionType(returnType, isVarArg = false, paramTypes = *paramTypes.toTypedArray()) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 965621d02b7..bd8b802d7f8 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -23,7 +23,6 @@ import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor -import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.types.KotlinType internal class CodeGenerator(override val context: Context) : ContextUtils { @@ -400,6 +399,17 @@ internal class CodeGenerator(override val context: Context) : ContextUtils { return res } + fun blockAddress(bbLabel: LLVMBasicBlockRef): LLVMValueRef { + return LLVMBlockAddress(function, bbLabel)!! + } + + fun indirectBr(address: LLVMValueRef, destinations: Collection): LLVMValueRef? { + val indirectBr = LLVMBuildIndirectBr(builder, address, destinations.size) + destinations.forEach { LLVMAddDestination(indirectBr, it) } + currentPositionHolder.setAfterTerminator() + return indirectBr + } + //-------------------------------------------------------------------------// fun gxxLandingpad(numClauses: Int, name: String = ""): LLVMValueRef { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 07ba15264c0..65dab306b5b 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -19,15 +19,19 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.common.descriptors.allParameters +import org.jetbrains.kotlin.backend.common.descriptors.isSuspend import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanConfigKeys import org.jetbrains.kotlin.backend.konan.KonanPhase import org.jetbrains.kotlin.backend.konan.PhaseManager import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.IrInlineFunctionBody +import org.jetbrains.kotlin.backend.konan.ir.IrSuspendableExpression +import org.jetbrains.kotlin.backend.konan.ir.IrSuspensionPoint import org.jetbrains.kotlin.backend.konan.ir.ir2string import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.declarations.* @@ -37,9 +41,11 @@ import org.jetbrains.kotlin.ir.util.getArguments import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassOrAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isNothing @@ -197,6 +203,7 @@ internal interface CodeContext { * @return the requested value */ fun fileScope(): CodeContext? + fun addResumePoint(bbLabel: LLVMBasicBlockRef) } //-------------------------------------------------------------------------// @@ -239,6 +246,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid override fun functionScope(): CodeContext? = null override fun fileScope(): CodeContext? = null + + override fun addResumePoint(bbLabel: LLVMBasicBlockRef) = unsupported(bbLabel) } /** @@ -329,6 +338,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } //-------------------------------------------------------------------------// + override fun visitFile(declaration: IrFile) { context.llvm.fileInitializers.clear() @@ -610,30 +620,33 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun evaluateExpression(value: IrExpression): LLVMValueRef { when (value) { - is IrTypeOperatorCall -> return evaluateTypeOperator (value) - is IrCall -> return evaluateCall (value) + is IrTypeOperatorCall -> return evaluateTypeOperator (value) + is IrCall -> return evaluateCall (value) is IrDelegatingConstructorCall -> - return evaluateCall(value) + return evaluateCall (value) is IrInstanceInitializerCall -> return evaluateInstanceInitializerCall(value) - is IrGetValue -> return evaluateGetValue (value) - is IrSetVariable -> return evaluateSetVariable (value) - is IrGetField -> return evaluateGetField (value) - is IrSetField -> return evaluateSetField (value) - is IrConst<*> -> return evaluateConst (value) - is IrReturn -> return evaluateReturn (value) - is IrWhen -> return evaluateWhen (value) - is IrThrow -> return evaluateThrow (value) - is IrTry -> return evaluateTry (value) - is IrInlineFunctionBody -> return evaluateInlineFunction (value) - is IrContainerExpression -> return evaluateContainerExpression(value) - is IrWhileLoop -> return evaluateWhileLoop (value) - is IrDoWhileLoop -> return evaluateDoWhileLoop (value) - is IrVararg -> return evaluateVararg (value) - is IrBreak -> return evaluateBreak (value) - is IrContinue -> return evaluateContinue (value) - is IrGetObjectValue -> return evaluateGetObjectValue (value) - is IrCallableReference -> return evaluateCallableReference (value) + is IrGetValue -> return evaluateGetValue (value) + is IrSetVariable -> return evaluateSetVariable (value) + is IrGetField -> return evaluateGetField (value) + is IrSetField -> return evaluateSetField (value) + is IrConst<*> -> return evaluateConst (value) + is IrReturn -> return evaluateReturn (value) + is IrWhen -> return evaluateWhen (value) + is IrThrow -> return evaluateThrow (value) + is IrTry -> return evaluateTry (value) + is IrInlineFunctionBody -> return evaluateInlineFunction (value) + is IrContainerExpression -> return evaluateContainerExpression (value) + is IrWhileLoop -> return evaluateWhileLoop (value) + is IrDoWhileLoop -> return evaluateDoWhileLoop (value) + is IrVararg -> return evaluateVararg (value) + is IrBreak -> return evaluateBreak (value) + is IrContinue -> return evaluateContinue (value) + is IrGetObjectValue -> return evaluateGetObjectValue (value) + is IrCallableReference -> return evaluateCallableReference (value) + is IrSuspendableExpression -> + return evaluateSuspendableExpression (value) + is IrSuspensionPoint -> return evaluateSuspensionPoint (value) else -> { TODO("${ir2string(value)}") } @@ -1722,6 +1735,23 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// + + private val coroutineImplDescriptor = context.builtIns.getKonanInternalClass("CoroutineImpl") + private val doResumeFunctionDescriptor = coroutineImplDescriptor.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("doResume"), NoLookupLocation.FROM_BACKEND).single() + + private fun getContinuation(): LLVMValueRef { + val caller = codegen.functionDescriptor!! + return if (caller.isSuspend) + codegen.param(caller, caller.allParameters.size) // The last argument. + else { + // Suspend call from non-suspend function - must be [CoroutineImpl]. + assert (doResumeFunctionDescriptor in caller.overriddenDescriptors, + { "Expected 'CoroutineImpl.doResume' but was '$caller'" }) + currentCodeContext.genGetValue(caller.dispatchReceiverParameter!!) // Coroutine itself is a continuation. + } + } + /** * Evaluates all arguments of [expression] that are explicitly represented in the IR. * Returns results in the same order as LLVM function expects, assuming that all explicit arguments @@ -1757,23 +1787,81 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// + private inner class SuspendableExpressionScope(val resumePoints: MutableList) : InnerScopeImpl() { + override fun addResumePoint(bbLabel: LLVMBasicBlockRef) { + resumePoints.add(bbLabel) + } + } + + private fun evaluateSuspendableExpression(expression: IrSuspendableExpression): LLVMValueRef { + val suspensionPointId = evaluateExpression(expression.suspensionPointId) + val bbStart = codegen.basicBlock("start") + val bbDispatch = codegen.basicBlock("dispatch") + + val resumePoints = mutableListOf() + using (SuspendableExpressionScope(resumePoints)) { + codegen.condBr(codegen.icmpEq(suspensionPointId, kIntPtrZero), bbStart, bbDispatch) + + codegen.positionAtEnd(bbStart) + val result = evaluateExpression(expression.result) + + codegen.appendingTo(bbDispatch) { + codegen.indirectBr(codegen.intToPtr(suspensionPointId, int8TypePtr), resumePoints) + } + return result + } + } + + private inner class SuspensionPointScope(val suspensionPointId: VariableDescriptor, + val bbResume: LLVMBasicBlockRef): InnerScopeImpl() { + override fun genGetValue(descriptor: ValueDescriptor): LLVMValueRef { + if (descriptor == suspensionPointId) + return codegen.ptrToInt(codegen.blockAddress(bbResume), LLVMInt64Type()!!) // TODO: intptr. + return super.genGetValue(descriptor) + } + } + + private fun evaluateSuspensionPoint(expression: IrSuspensionPoint): LLVMValueRef { + val bbResume = codegen.basicBlock("resume") + currentCodeContext.addResumePoint(bbResume) + + using (SuspensionPointScope(expression.suspensionPointIdParameter.descriptor, bbResume)) { + continuationBlock(expression.type).run { + val normalResult = evaluateExpression(expression.result) + jump(this, normalResult) + + codegen.positionAtEnd(bbResume) + val resumeResult = evaluateExpression(expression.resumeResult) + jump(this, resumeResult) + + codegen.positionAtEnd(this.block) + return this.value + } + } + } + + //-------------------------------------------------------------------------// + private fun evaluateFunctionCall(callee: IrCall, args: List, resultLifetime: Lifetime): LLVMValueRef { - val descriptor:FunctionDescriptor = callee.descriptor as FunctionDescriptor + val descriptor = callee.descriptor as FunctionDescriptor + val argsWithContinuationIfNeeded = if (descriptor.isSuspend) + args + getContinuation() + else args if (descriptor.isFunctionInvoke) { - return evaluateFunctionInvoke(descriptor, args, resultLifetime) + return evaluateFunctionInvoke(descriptor, argsWithContinuationIfNeeded, resultLifetime) } if (descriptor.isIntrinsic) { - return evaluateIntrinsicCall(callee, args) + return evaluateIntrinsicCall(callee, argsWithContinuationIfNeeded) } when (descriptor) { - is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, args) - is ConstructorDescriptor -> return evaluateConstructorCall (callee, args) + is IrBuiltinOperatorDescriptorBase -> return evaluateOperatorCall (callee, argsWithContinuationIfNeeded) + is ConstructorDescriptor -> return evaluateConstructorCall (callee, argsWithContinuationIfNeeded) else -> return evaluateSimpleFunctionCall( - descriptor, args, resultLifetime, callee.superQualifier) + descriptor, argsWithContinuationIfNeeded, resultLifetime, callee.superQualifier) } } @@ -1870,6 +1958,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid codegen.icmpEq(arg0, arg1) } } + "konan.internal.getContinuation" -> return getContinuation() } val interop = context.interopBuiltIns @@ -1913,6 +2002,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!! private val kTrue = LLVMConstInt(LLVMInt1Type(), 1, 1)!! private val kFalse = LLVMConstInt(LLVMInt1Type(), 0, 1)!! + private val kIntPtrZero = LLVMConstInt(codegen.intPtrType, 0, 1)!! private fun evaluateOperatorCall(callee: IrCall, args: List): LLVMValueRef {