diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index df3fad4899a..d12dbeab9ac 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -2633,15 +2633,14 @@ public class ExpressionCodegen extends KtVisitor impleme } if (maybeSuspensionPoint) { - addSuspendMarker(v, suspensionPointKind, true); + addSuspendMarker(v, true, suspensionPointKind == SuspensionPointKind.NOT_INLINE); } callGenerator.genCall(callableMethod, resolvedCall, defaultMaskWasGenerated, this); if (maybeSuspensionPoint) { addReturnsUnitMarkerIfNecessary(v, resolvedCall); - - addSuspendMarker(v, suspensionPointKind, false); + addSuspendMarker(v, false, suspensionPointKind == SuspensionPointKind.NOT_INLINE); addInlineMarker(v, false); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt index 0e1f731d584..23df88915a8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/AnonymousObjectTransformer.kt @@ -144,8 +144,7 @@ class AnonymousObjectTransformer( inliningContext, classBuilder, methodsToTransform, - superClassName, - allCapturedParamBuilder.listCaptured() + superClassName ) loop@ for (next in methodsToTransform) { val deferringVisitor = @@ -526,7 +525,7 @@ class AnonymousObjectTransformer( alreadyAddedParam?.newFieldName ?: getNewFieldName(desc.fieldName, false), alreadyAddedParam != null ) - if (info is ExpressionLambda && info.isCapturedSuspend(desc, inliningContext)) { + if (info is ExpressionLambda && info.isCapturedSuspend(desc)) { recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend } val composed = StackValue.field( diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt index f78d519e412..8a12877e512 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/LambdaInfo.kt @@ -65,7 +65,10 @@ abstract class LambdaInfo(@JvmField val isCrossInline: Boolean) : FunctionalArgu for (info in capturedVars) { val field = remapper.findField(FieldInsnNode(0, info.containingLambdaName, info.fieldName, "")) ?: error("Captured field not found: " + info.containingLambdaName + "." + info.fieldName) - builder.addCapturedParam(field, info.fieldName) + val recapturedParamInfo = builder.addCapturedParam(field, info.fieldName) + if (this is ExpressionLambda && isCapturedSuspend(info)) { + recapturedParamInfo.functionalArgument = NonInlineableArgumentForInlineableParameterCalledInSuspend + } } return builder.buildParameters() @@ -226,7 +229,7 @@ abstract class ExpressionLambda(isCrossInline: Boolean) : LambdaInfo(isCrossInli } abstract fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor - abstract fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean + abstract fun isCapturedSuspend(desc: CapturedParamDesc): Boolean } class PsiExpressionLambda( @@ -341,6 +344,6 @@ class PsiExpressionLambda( ) } - override fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean = - isCapturedSuspendLambda(closure, desc.fieldName, inliningContext.state.bindingContext) + override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean = + isCapturedSuspendLambda(closure, desc.fieldName, typeMapper.bindingContext) } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt index e98ea7507b0..bead579a354 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/MethodInliner.kt @@ -15,7 +15,6 @@ import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationM import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.top -import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.config.LanguageFeature import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.ParameterDescriptor @@ -216,10 +215,8 @@ class MethodInliner( val info = invokeCall.functionalArgument if (info !is LambdaInfo) { - if (info == NonInlineableArgumentForInlineableSuspendParameter) { - super.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, NOINLINE_CALL_MARKER, "()V", false) - } //noninlinable lambda + markNoinlineLambdaIfSuspend(mv, info) super.visitMethodInsn(opcode, owner, name, desc, itf) return } @@ -363,8 +360,6 @@ class MethodInliner( ReifiedTypeInliner.isNeedClassReificationMarker(MethodInsnNode(opcode, owner, name, desc, false)) ) { //we shouldn't process here content of inlining lambda it should be reified at external level except default lambdas - } else if (owner == NOINLINE_CALL_MARKER && name == NOINLINE_CALL_MARKER) { - // do not generate multiple markers on single invoke } else { super.visitMethodInsn(opcode, owner, name, desc, itf) } @@ -385,21 +380,8 @@ class MethodInliner( node.accept(lambdaInliner) - return surroundInvokesWithSuspendMarkersIfNeeded(resultNode) - } - - private fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode): MethodNode { - val markers = node.instructions.asSequence().filter { it.isNoinlineCallMarker() }.toList() - if (markers.isEmpty()) return node - val invokes = markers.map { it.next as MethodInsnNode } - node.instructions.removeAll(markers) - - val sourceFrames = MethodTransformer.analyze(inlineCallSiteInfo.ownerClassName, node, SourceInterpreter()) - val toSurround = invokes.mapNotNull { insn -> - findReceiverOfInvoke(sourceFrames[node.instructions.indexOf(insn)], insn)?.let { insn to it } - } - surroundInvokesWithSuspendMarkers(node, toSurround) - return node + surroundInvokesWithSuspendMarkersIfNeeded(resultNode) + return resultNode } private fun isDefaultLambdaWithReification(lambdaInfo: LambdaInfo) = @@ -901,6 +883,8 @@ class MethodInliner( getFunctionalArgumentIfExists((insnNode as VarInsnNode).`var`) insnNode is FieldInsnNode && insnNode.name.startsWith(CAPTURED_FIELD_FOLD_PREFIX) -> findCapturedField(insnNode, nodeRemapper).functionalArgument + insnNode is FieldInsnNode && insnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext) -> + NonInlineableArgumentForInlineableParameterCalledInSuspend else -> null } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt index 0cefda1054c..0b2c978d2af 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/coroutines/CoroutineTransformer.kt @@ -6,36 +6,28 @@ package org.jetbrains.kotlin.codegen.inline.coroutines import com.intellij.util.ArrayUtil -import org.jetbrains.kotlin.codegen.AsmUtil.CAPTURED_THIS_FIELD import org.jetbrains.kotlin.codegen.ClassBuilder -import org.jetbrains.kotlin.codegen.TransformationMethodVisitor import org.jetbrains.kotlin.codegen.coroutines.* import org.jetbrains.kotlin.codegen.inline.* import org.jetbrains.kotlin.codegen.optimization.common.asSequence -import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer -import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer import org.jetbrains.kotlin.config.isReleaseCoroutines import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin -import org.jetbrains.kotlin.utils.addToStdlib.cast import org.jetbrains.org.objectweb.asm.* import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue -const val NOINLINE_CALL_MARKER = "NOINLINE_CALL_MARKER" - const val FOR_INLINE_SUFFIX = "\$\$forInline" class CoroutineTransformer( private val inliningContext: InliningContext, private val classBuilder: ClassBuilder, private val methods: List, - private val superClassName: String, - private val capturedParams: List + private val superClassName: String ) { private val state = inliningContext.state // If we inline into inline function, we should generate both method with state-machine for Java interop and method without @@ -89,21 +81,18 @@ class CoroutineTransformer( ) ) { val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline - val stateMachineBuilder = surroundNoinlineCallsWithMarkers( - node, - CoroutineTransformerMethodVisitor( - createNewMethodFrom(node, name), node.access, name, node.desc, null, null, - obtainClassBuilderForCoroutineState = { classBuilder }, - reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) }, - // TODO: this linenumbers might not be correct and since they are used only for step-over, check them. - lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber, - sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "", - languageVersionSettings = state.languageVersionSettings, - shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, - containingClassInternalName = classBuilder.thisName, - isForNamedFunction = false, - disableTailCallOptimizationForFunctionReturningUnit = false - ) + val stateMachineBuilder = CoroutineTransformerMethodVisitor( + createNewMethodFrom(node, name), node.access, name, node.desc, null, null, + obtainClassBuilderForCoroutineState = { classBuilder }, + reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) }, + // TODO: this linenumbers might not be correct and since they are used only for step-over, check them. + lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber, + sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "", + languageVersionSettings = state.languageVersionSettings, + shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, + containingClassInternalName = classBuilder.thisName, + isForNamedFunction = false, + disableTailCallOptimizationForFunctionReturningUnit = false ) if (generateForInline) @@ -126,23 +115,20 @@ class CoroutineTransformer( // If the node already has state-machine, it is safer to generate state-machine. val disableTailCallOptimization = methods.find { it.name == name && it.desc == node.desc }?.let { isStateMachine(it) } ?: false val sourceCompilerForInline = inliningContext.root.sourceCompilerForInline - val stateMachineBuilder = surroundNoinlineCallsWithMarkers( - node, - CoroutineTransformerMethodVisitor( - createNewMethodFrom(node, name), node.access, name, node.desc, null, null, - obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! }, - reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) }, - lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber, - sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "", - languageVersionSettings = state.languageVersionSettings, - shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, - containingClassInternalName = classBuilder.thisName, - isForNamedFunction = true, - needDispatchReceiver = true, - internalNameForDispatchReceiver = classBuilder.thisName, - disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization, - putContinuationParameterToLvt = !state.isIrBackend - ) + val stateMachineBuilder = CoroutineTransformerMethodVisitor( + createNewMethodFrom(node, name), node.access, name, node.desc, null, null, + obtainClassBuilderForCoroutineState = { (inliningContext as RegeneratedClassContext).continuationBuilders[continuationClassName]!! }, + reportSuspensionPointInsideMonitor = { sourceCompilerForInline.reportSuspensionPointInsideMonitor(it) }, + lineNumber = sourceCompilerForInline.inlineCallSiteInfo.lineNumber, + sourceFile = sourceCompilerForInline.callsiteFile?.name ?: "", + languageVersionSettings = state.languageVersionSettings, + shouldPreserveClassInitialization = state.constructorCallNormalizationMode.shouldPreserveClassInitialization, + containingClassInternalName = classBuilder.thisName, + isForNamedFunction = true, + needDispatchReceiver = true, + internalNameForDispatchReceiver = classBuilder.thisName, + disableTailCallOptimizationForFunctionReturningUnit = disableTailCallOptimization, + putContinuationParameterToLvt = !state.isIrBackend ) if (generateForInline) @@ -152,35 +138,6 @@ class CoroutineTransformer( } } - private fun surroundNoinlineCallsWithMarkers(node: MethodNode, delegate: MethodVisitor): MethodVisitor = - SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor( - delegate, node.access, node.name, node.desc, classBuilder.thisName, this::fieldIsCapturedSuspendLambda - ) - - private fun fieldIsCapturedSuspendLambda(field: FieldInsnNode): Boolean = - capturedParams.find { it.newFieldName == field.name }?.let { it.functionalArgument?.isSuspendLambda() == true } - ?: isSuspendLambdaCapturedByOuterObjectOrLambda(field) - - // We cannot find the lambda in captured parameters: it came from object outside of the our reach: - // this can happen when the lambda capture by non-transformed closure: - // inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() } - // inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() } - // Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0) - // Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode - // ALOAD 0 - // GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1; - // GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1; - // Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context: - private fun isSuspendLambdaCapturedByOuterObjectOrLambda(field: FieldInsnNode): Boolean { - val functionDescriptor = inliningContext.root.sourceCompilerForInline.compilationContextFunctionDescriptor - val classDescriptor = functionDescriptor.findContainingClassOrLambda() ?: return false - return isCapturedSuspendLambda(classDescriptor, field.name, inliningContext.state.bindingContext) - } - - private tailrec fun DeclarationDescriptor.findContainingClassOrLambda(): ClassDescriptor? = - if (containingDeclaration is ClassDescriptor) containingDeclaration as ClassDescriptor - else containingDeclaration?.findContainingClassOrLambda() - private fun createNewMethodFrom(node: MethodNode, name: String): MethodVisitor { return classBuilder.newMethod( JvmDeclarationOrigin.NO_ORIGIN, node.access, name, node.desc, node.signature, ArrayUtil.toStringArray(node.exceptions) @@ -219,77 +176,71 @@ class CoroutineTransformer( } } -class SurroundSuspendLambdaCallsWithSuspendMarkersMethodVisitor( - delegate: MethodVisitor, access: Int, name: String, desc: String, - private val thisName: String, - private val isCapturedSuspendLambda: (FieldInsnNode) -> Boolean -) : TransformationMethodVisitor(delegate, access, name, desc, null, null) { - override fun performTransformations(methodNode: MethodNode) { - fun AbstractInsnNode.index() = methodNode.instructions.indexOf(this) +private const val NOINLINE_CALL_MARKER = "\$\$\$\$\$NOINLINE_CALL_MARKER\$\$\$\$\$" - FixStackMethodTransformer().transform(thisName, methodNode) - val sourceFrames = MethodTransformer.analyze(thisName, methodNode, SourceInterpreter()) - - val noinlineInvokes = arrayListOf>() - - for (insn in methodNode.instructions.asSequence()) { - if (insn.opcode != Opcodes.INVOKEINTERFACE) continue - insn as MethodInsnNode - if (!isInvokeOnLambda(insn.owner, insn.name)) continue - val frame = sourceFrames[insn.index()] ?: continue - val receiver = findReceiverOfInvoke(frame, insn).takeIf { it?.isSuspendLambda(insn) == true } as? FieldInsnNode ?: continue - val aload = receiver.findPreviousOrNull { it.opcode != Opcodes.GETFIELD } ?: error("GETFIELD cannot be the first instruction") - assert(aload.opcode == Opcodes.ALOAD) { "Before GETFIELD there shall be ALOAD" } - noinlineInvokes.add(insn to aload) - } - - surroundInvokesWithSuspendMarkers(methodNode, noinlineInvokes) - } - - private fun AbstractInsnNode.isSuspendLambda(invoke: MethodInsnNode): Boolean { - if (opcode != Opcodes.GETFIELD) return false - this as FieldInsnNode - if (desc != "L${invoke.owner};") return false - var current: FieldInsnNode? = this - // Unroll the battery of - // GETFIELD .this$0 L; - // GETFIELD .this$0 L; - // ... - // GETFIELD .$action Lkotlin/jvm/functions/FunctionM; - while (current != null) { - if (current.owner == thisName) break - if (current.previous?.opcode != Opcodes.GETFIELD || current.previous.cast().name != CAPTURED_THIS_FIELD) return false - current = current.previous as FieldInsnNode - } - return isCapturedSuspendLambda(this) +fun markNoinlineLambdaIfSuspend(mv: MethodVisitor, info: FunctionalArgument?) { + when (info) { + NonInlineableArgumentForInlineableSuspendParameter -> + mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "always", "()V", false) + NonInlineableArgumentForInlineableParameterCalledInSuspend -> + mv.visitMethodInsn(Opcodes.INVOKESTATIC, NOINLINE_CALL_MARKER, "conditional", "()V", false) } } -private fun FunctionalArgument.isSuspendLambda(): Boolean = - this is NonInlineableArgumentForInlineableParameterCalledInSuspend || (this is ExpressionLambda && isSuspend) +private fun Frame.getSource(offset: Int): AbstractInsnNode? = + getStack(stackSize - offset - 1)?.insns?.singleOrNull() -fun surroundInvokesWithSuspendMarkers( - methodNode: MethodNode, - noinlineInvokes: List> -) { - for ((invoke, aload) in noinlineInvokes) { - // Generate inline markers for stack transformation. It is required for local variables spilling. - methodNode.instructions.insertBefore(aload, withInstructionAdapter { +fun surroundInvokesWithSuspendMarkersIfNeeded(node: MethodNode) { + val markers = node.instructions.asSequence().filter { + it.opcode == Opcodes.INVOKESTATIC && (it as MethodInsnNode).owner == NOINLINE_CALL_MARKER + }.toList() + if (markers.isEmpty()) return + + val sourceFrames = MethodTransformer.analyze("fake", node, SourceInterpreter()) + val loads = markers.map { marker -> + val arity = (marker.next as MethodInsnNode).owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt() + var receiver = sourceFrames[node.instructions.indexOf(marker) + 1].getSource(arity) + // Navigate the ALOAD+GETFIELD+... chain to the first instruction. We need to insert a stack + // spilling marker before it starts. + while (receiver?.opcode == Opcodes.GETFIELD) { + receiver = sourceFrames[node.instructions.indexOf(receiver)].getSource(0) + } + receiver + } + for ((marker, load) in markers.zip(loads)) { + val conditional = (marker as MethodInsnNode).name == "conditional" + val invoke = marker.next as MethodInsnNode + node.instructions.remove(marker) + if (load == null) { + continue // dead code, doesn't matter + } + node.instructions.insertBefore(load, withInstructionAdapter { addInlineMarker(this, isStartNotEnd = true) }) - methodNode.instructions.insertBefore(invoke, withInstructionAdapter { - addSuspendMarker(this, isStartNotEnd = true) + node.instructions.insertBefore(invoke, withInstructionAdapter { + addSuspendMarker(this, isStartNotEnd = true, inlinable = conditional) }) - methodNode.instructions.insert(invoke, withInstructionAdapter { - addSuspendMarker(this, isStartNotEnd = false) + node.instructions.insert(invoke, withInstructionAdapter { + addSuspendMarker(this, isStartNotEnd = false, inlinable = conditional) addInlineMarker(this, isStartNotEnd = false) }) } } -// TODO: What to do if suddenly there are not exactly one receiver? -fun findReceiverOfInvoke(frame: Frame, insn: MethodInsnNode): AbstractInsnNode? = - frame.getStack(frame.stackSize - insn.owner.removePrefix(NUMBERED_FUNCTION_PREFIX).toInt() - 1)?.insns?.singleOrNull() - -fun AbstractInsnNode.isNoinlineCallMarker(): Boolean = - opcode == Opcodes.INVOKESTATIC && cast().let { it.owner == NOINLINE_CALL_MARKER && it.name == NOINLINE_CALL_MARKER } +// We cannot find the lambda in captured parameters: it came from object outside of the our reach: +// this can happen when the lambda capture by non-transformed closure: +// inline fun inlineMe(crossinline c: suspend() -> Unit) = suspend { c() } +// inline fun inlineMe2(crossinline c: suspend() -> Unit) = suspend { inlineMe { c() }() } +// Suppose, we inline inlineMe into inlineMe2: the only knowledge we have about inlineMe$1 is captured receiver (this$0) +// Thus, transformed lambda from inlineMe, inlineMe3$$inlined$inlineMe2$1 contains the following bytecode +// ALOAD 0 +// GETFIELD inlineMe2$1$invokeSuspend$$inlined$inlineMe$1.this$0 : LScratchKt$inlineMe2$1; +// GETFIELD inlineMe2$1.$c : Lkotlin/jvm/functions/Function1; +// Since inlineMe2's lambda is outside of reach of the inliner, find crossinline parameter from compilation context: +fun FieldInsnNode.isSuspendLambdaCapturedByOuterObjectOrLambda(inliningContext: InliningContext): Boolean { + var container: DeclarationDescriptor = inliningContext.root.sourceCompilerForInline.compilationContextFunctionDescriptor + while (container !is ClassDescriptor) { + container = container.containingDeclaration ?: return false + } + return isCapturedSuspendLambda(container, name, inliningContext.state.bindingContext) +} diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt index dde4227c56e..cc085e68f15 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.codegen.context.CodegenContext import org.jetbrains.kotlin.codegen.context.CodegenContextUtil import org.jetbrains.kotlin.codegen.context.InlineLambdaContext import org.jetbrains.kotlin.codegen.context.MethodContext -import org.jetbrains.kotlin.codegen.coroutines.SuspensionPointKind import org.jetbrains.kotlin.codegen.coroutines.unwrapInitialDescriptorForSuspendFunction import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence import org.jetbrains.kotlin.codegen.optimization.common.asSequence @@ -432,18 +431,14 @@ fun addReturnsUnitMarker(v: InstructionAdapter) { v.emitInlineMarker(INLINE_MARKER_RETURNS_UNIT) } -fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { - v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_SUSPEND_ID else INLINE_MARKER_AFTER_SUSPEND_ID) -} - -fun addInlineSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { - v.emitInlineMarker(if (isStartNotEnd) INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID else INLINE_MARKER_AFTER_INLINE_SUSPEND_ID) -} - -fun addSuspendMarker(v: InstructionAdapter, kind: SuspensionPointKind, isStartNotEnd: Boolean) = when (kind) { - SuspensionPointKind.NEVER -> Unit - SuspensionPointKind.NOT_INLINE -> addInlineSuspendMarker(v, isStartNotEnd) - SuspensionPointKind.ALWAYS -> addSuspendMarker(v, isStartNotEnd) +fun addSuspendMarker(v: InstructionAdapter, isStartNotEnd: Boolean, inlinable: Boolean = false) { + val marker = when { + inlinable && isStartNotEnd -> INLINE_MARKER_BEFORE_INLINE_SUSPEND_ID + inlinable -> INLINE_MARKER_AFTER_INLINE_SUSPEND_ID + isStartNotEnd -> INLINE_MARKER_BEFORE_SUSPEND_ID + else -> INLINE_MARKER_AFTER_SUSPEND_ID + } + v.emitInlineMarker(marker) } fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isStartNotEnd: Boolean) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt index 83ab1b2d2dc..13e35722e00 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/ExpressionCodegen.kt @@ -377,6 +377,10 @@ class ExpressionCodegen( val asmType = if (expression is IrConstructorCall) typeMapper.mapTypeAsDeclaration(expression.type) else expression.asmType val isSuspensionPoint = expression.isSuspensionPoint() + if (isSuspensionPoint != SuspensionPointKind.NEVER) { + addInlineMarker(mv, isStartNotEnd = true) + } + when { expression is IrConstructorCall -> { closureReifiedMarkers[expression.symbol.owner.parentAsClass]?.let { @@ -404,8 +408,6 @@ class ExpressionCodegen( } expression.symbol.descriptor is ConstructorDescriptor -> throw AssertionError("IrCall with ConstructorDescriptor: ${expression.javaClass.simpleName}") - isSuspensionPoint != SuspensionPointKind.NEVER -> - addInlineMarker(mv, isStartNotEnd = true) } expression.dispatchReceiver?.let { receiver -> @@ -430,7 +432,7 @@ class ExpressionCodegen( expression.markLineNumber(true) if (isSuspensionPoint != SuspensionPointKind.NEVER) { - addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = true) + addSuspendMarker(mv, isStartNotEnd = true, isSuspensionPoint == SuspensionPointKind.NOT_INLINE) } if (irFunction.isInvokeSuspendOfContinuation()) { @@ -443,7 +445,7 @@ class ExpressionCodegen( } if (isSuspensionPoint != SuspensionPointKind.NEVER) { - addSuspendMarker(mv, isSuspensionPoint, isStartNotEnd = false) + addSuspendMarker(mv, isStartNotEnd = false, isSuspensionPoint == SuspensionPointKind.NOT_INLINE) addInlineMarker(mv, isStartNotEnd = false) } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt index 1bf921a3ac1..795fcf15682 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/IrInlineCodegen.kt @@ -250,7 +250,7 @@ class IrExpressionLambdaImpl( override fun getInlineSuspendLambdaViewDescriptor(): FunctionDescriptor = function.descriptor - override fun isCapturedSuspend(desc: CapturedParamDesc, inliningContext: InliningContext): Boolean = + override fun isCapturedSuspend(desc: CapturedParamDesc): Boolean = capturedParameters[desc]?.let { it.isInlineParameter() && it.type.isSuspendFunctionTypeOrSubtype() } == true } diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation.txt b/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation.txt index c57f0b37497..03a2ecb0743 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation.txt @@ -68,11 +68,33 @@ public final class TcoContinuationKt$foo$$inlined$flow$1 { public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object } +@kotlin.Metadata +public final class TcoContinuationKt$foo$$inlined$map$1$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object + synthetic field data: java.lang.Object + synthetic field exception: java.lang.Throwable + synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2$1 + public method (p0: TcoContinuationKt$foo$$inlined$map$1$2, p1: kotlin.coroutines.experimental.Continuation): void + public final @org.jetbrains.annotations.Nullable method doResume(@org.jetbrains.annotations.Nullable p0: java.lang.Object, @org.jetbrains.annotations.Nullable p1: java.lang.Throwable): java.lang.Object + synthetic final method getLabel(): int + synthetic final method setLabel(p0: int): void +} + @kotlin.Metadata public final class TcoContinuationKt$foo$$inlined$map$1$2 { synthetic final field $this_flow$inlined: FlowCollector synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1 inner class TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2$1 public method (p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void public @org.jetbrains.annotations.Nullable method emit(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.experimental.Continuation): java.lang.Object } @@ -100,6 +122,14 @@ public final class TcoContinuationKt$map$$inlined$transform$1$1 { @kotlin.Metadata public final class TcoContinuationKt$map$$inlined$transform$1$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2 @@ -148,6 +178,14 @@ public final class TcoContinuationKt$map$$inlined$transform$2$1 { @kotlin.Metadata public final class TcoContinuationKt$map$$inlined$transform$2$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object synthetic field data: java.lang.Object synthetic field exception: java.lang.Throwable synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2 diff --git a/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation_1_3.txt b/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation_1_3.txt index 2a11991a038..6c5a0443cf8 100644 --- a/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation_1_3.txt +++ b/compiler/testData/codegen/bytecodeListing/coroutines/tcoContinuation_1_3.txt @@ -64,11 +64,32 @@ public final class TcoContinuationKt$foo$$inlined$flow$1 { public @org.jetbrains.annotations.Nullable method collect(@org.jetbrains.annotations.NotNull p0: FlowCollector, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object } +@kotlin.Metadata +@kotlin.coroutines.jvm.internal.DebugMetadata +public final class TcoContinuationKt$foo$$inlined$map$1$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object + field label: int + synthetic field result: java.lang.Object + synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2$1 + public method (p0: TcoContinuationKt$foo$$inlined$map$1$2, p1: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object +} + @kotlin.Metadata public final class TcoContinuationKt$foo$$inlined$map$1$2 { synthetic final field $this_flow$inlined: FlowCollector synthetic final field this$0: TcoContinuationKt$foo$$inlined$map$1 inner class TcoContinuationKt$foo$$inlined$map$1$2 + inner class TcoContinuationKt$foo$$inlined$map$1$2$1 public method (p0: FlowCollector, p1: TcoContinuationKt$foo$$inlined$map$1): void public @org.jetbrains.annotations.Nullable method emit(p0: java.lang.Object, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object } @@ -93,7 +114,16 @@ public final class TcoContinuationKt$map$$inlined$transform$1$1 { } @kotlin.Metadata +@kotlin.coroutines.jvm.internal.DebugMetadata public final class TcoContinuationKt$map$$inlined$transform$1$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$1$2 @@ -137,7 +167,16 @@ public final class TcoContinuationKt$map$$inlined$transform$2$1 { } @kotlin.Metadata +@kotlin.coroutines.jvm.internal.DebugMetadata public final class TcoContinuationKt$map$$inlined$transform$2$2$1 { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field L$3: java.lang.Object + field L$4: java.lang.Object + field L$5: java.lang.Object + field L$6: java.lang.Object + field L$7: java.lang.Object field label: int synthetic field result: java.lang.Object synthetic final field this$0: TcoContinuationKt$map$$inlined$transform$2$2