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 4995466663b..1114405cb6d 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -416,7 +416,7 @@ internal fun addFakeContinuationConstructorCallMarker(v: InstructionAdapter, isS * In such cases we just generate the marker which is going to be replaced with real continuation on generating state machine. * See [CoroutineTransformerMethodVisitor] for more info. */ -internal fun addFakeContinuationMarker(v: InstructionAdapter) { +fun addFakeContinuationMarker(v: InstructionAdapter) { v.emitInlineMarker(INLINE_MARKER_FAKE_CONTINUATION) v.aconst(null) } 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 c46d72f82a6..7f125a7e290 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 @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.ir.Ir import org.jetbrains.kotlin.backend.common.phaser.PhaseConfig import org.jetbrains.kotlin.backend.jvm.codegen.IrTypeMapper import org.jetbrains.kotlin.backend.jvm.codegen.MethodSignatureMapper +import org.jetbrains.kotlin.backend.jvm.codegen.createFakeContinuation import org.jetbrains.kotlin.backend.jvm.descriptors.JvmDeclarationFactory import org.jetbrains.kotlin.backend.jvm.descriptors.JvmSharedVariablesManager import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods @@ -24,6 +25,7 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns +import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.symbols.IrClassSymbol import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrLocalDelegatedPropertySymbol @@ -84,6 +86,7 @@ class JvmBackendContext( val suspendLambdaToOriginalFunctionMap = mutableMapOf() val continuationClassBuilders = mutableMapOf() val suspendFunctionViews = mutableMapOf() + val FAKE_CONTINUATION: IrExpression = createFakeContinuation(this) val staticDefaultStubs = mutableMapOf() diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt index 72eb7eab238..7ccf28fe7d4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/codegen/CoroutineCodegen.kt @@ -27,6 +27,7 @@ import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrGetValue import org.jetbrains.kotlin.ir.expressions.copyTypeArgumentsFrom import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl import org.jetbrains.kotlin.ir.types.createType @@ -146,13 +147,17 @@ private fun IrFunction.suspendFunctionView(context: JvmBackendContext): IrFuncti override fun visitCall(expression: IrCall): IrExpression { if (!expression.isSuspend) return super.visitCall(expression) - return super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it)) + return super.visitCall(expression.createSuspendFunctionCallViewIfNeeded(context, it, callerIsInlineLambda = false)) } }) } } -internal fun IrCall.createSuspendFunctionCallViewIfNeeded(context: JvmBackendContext, caller: IrFunction): IrCall { +internal fun IrCall.createSuspendFunctionCallViewIfNeeded( + context: JvmBackendContext, + caller: IrFunction, + callerIsInlineLambda: Boolean +): IrCall { if (!isSuspend) return this val view = (symbol.owner as IrSimpleFunction).getOrCreateSuspendFunctionViewIfNeeded(context) if (view == symbol.owner) return this @@ -164,9 +169,19 @@ internal fun IrCall.createSuspendFunctionCallViewIfNeeded(context: JvmBackendCon it.putValueArgument(i, getValueArgument(i)) } val continuationParameter = - if (caller.isInvokeSuspendOfLambda(context) || caller.isInvokeSuspendOfContinuation(context)) caller.dispatchReceiverParameter!! - else caller.valueParameters.last() - val continuation = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, continuationParameter.symbol) - it.putValueArgument(valueArgumentsCount, continuation) + when { + caller.isInvokeSuspendOfLambda(context) || caller.isInvokeSuspendOfContinuation(context) -> + IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.dispatchReceiverParameter!!.symbol) + callerIsInlineLambda -> context.FAKE_CONTINUATION + else -> IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, caller.valueParameters.last().symbol) + } + it.putValueArgument(valueArgumentsCount, continuationParameter) } -} \ No newline at end of file +} + +internal fun createFakeContinuation(context: JvmBackendContext): IrExpression = IrErrorExpressionImpl( + UNDEFINED_OFFSET, + UNDEFINED_OFFSET, + context.ir.symbols.continuationClass.createType(true, listOf(makeTypeProjection(context.irBuiltIns.anyNType, Variance.INVARIANT))), + "FAKE_CONTINUATION" +) 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 bdec2c57019..058ef65d0b5 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 @@ -158,7 +158,11 @@ class ExpressionCodegen( // TODO remove fun gen(expression: IrExpression, type: Type, irType: IrType, data: BlockInfo): StackValue { - expression.accept(this, data).coerce(type, irType).materialize() + if (expression === context.FAKE_CONTINUATION) { + addFakeContinuationMarker(mv) + } else { + expression.accept(this, data).coerce(type, irType).materialize() + } return StackValue.onStack(type, irType.toKotlinType()) } @@ -299,7 +303,7 @@ class ExpressionCodegen( visitStatementContainer(expression, data).coerce(expression.type) override fun visitCall(expression: IrCall, data: BlockInfo): PromisedValue { - return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction), data) + return visitFunctionAccess(expression.createSuspendFunctionCallViewIfNeeded(context, irFunction, isInlineLambda), data) } override fun visitFunctionAccess(expression: IrFunctionAccessExpression, data: BlockInfo): PromisedValue { @@ -666,10 +670,12 @@ class ExpressionCodegen( } arity == 2 && expression.arguments[0].type.isStringClassType() -> { // Call the stringPlus intrinsic - for (argument in expression.arguments) { + for ((index, argument) in expression.arguments.withIndex()) { val result = argument.accept(this, data).boxInlineClasses(argument.type).materialized if (result.type.sort != Type.OBJECT) { result.genToString(mv) + } else if (index == 0) { + result.coerce(context.irBuiltIns.stringType).materialize() } } mv.invokestatic( diff --git a/compiler/testData/codegen/box/coroutines/controlFlow/forWithStep.kt b/compiler/testData/codegen/box/coroutines/controlFlow/forWithStep.kt index bfb537db413..cf85b08bcdc 100644 --- a/compiler/testData/codegen/box/coroutines/controlFlow/forWithStep.kt +++ b/compiler/testData/codegen/box/coroutines/controlFlow/forWithStep.kt @@ -1,5 +1,4 @@ // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/inlinedTryCatchFinally.kt b/compiler/testData/codegen/box/coroutines/inlinedTryCatchFinally.kt index 451674648f9..d66f21d0203 100644 --- a/compiler/testData/codegen/box/coroutines/inlinedTryCatchFinally.kt +++ b/compiler/testData/codegen/box/coroutines/inlinedTryCatchFinally.kt @@ -1,5 +1,4 @@ // KJS_WITH_FULL_RUNTIME -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/kt15016.kt b/compiler/testData/codegen/box/coroutines/kt15016.kt index 0221e025cf1..864ba110a29 100644 --- a/compiler/testData/codegen/box/coroutines/kt15016.kt +++ b/compiler/testData/codegen/box/coroutines/kt15016.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/suspendFromInlineLambda.kt b/compiler/testData/codegen/box/coroutines/suspendFromInlineLambda.kt index 3e6a253bba2..de1171d1037 100644 --- a/compiler/testData/codegen/box/coroutines/suspendFromInlineLambda.kt +++ b/compiler/testData/codegen/box/coroutines/suspendFromInlineLambda.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // WITH_RUNTIME // WITH_COROUTINES // COMMON_COROUTINES_TEST diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/also.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/also.kt index ad7018cd04a..405456088cd 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/also.kt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/also.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FULL_JDK // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt new file mode 100644 index 00000000000..0a4a197ea86 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt @@ -0,0 +1,42 @@ +// WITH_RUNTIME +// WITH_COROUTINES +// IGNORE_BACKEND: JVM +// TARGET_BACKEND: JVM +import helpers.* +// CHECK_BYTECODE_LISTING +// CHECK_NEW_COUNT: function=suspendHere count=0 +// CHECK_NEW_COUNT: function=complexSuspend count=0 +import kotlin.coroutines.* +import kotlin.coroutines.intrinsics.* + +inline suspend fun suspendThere(v: String): String = suspendCoroutineUninterceptedOrReturn { x -> + x.resume(v) + COROUTINE_SUSPENDED +} + +// TODO: Somehow we still generate continuations for tail-call function, but we don't use them. +suspend fun suspendHere(): String = suspendThere("O") + +// There is a kind of redundant state machine generated for complexSuspend: +// it's basically has the only suspend call just before return, but there is +// a redundant CHECKCAST String in the run's lambda, so we have to insert the state machine. +// TODO: Think of avoiding such redundant casts +suspend fun complexSuspend(): String { + return run { + suspendThere("K") + } +} + +fun builder(c: suspend () -> Unit) { + c.startCoroutine(EmptyContinuation) +} + +fun box(): String { + var result = "" + + builder { + result = suspendHere() + complexSuspend() + } + + return result +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.txt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.txt new file mode 100644 index 00000000000..10ad9ad9a95 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.txt @@ -0,0 +1,48 @@ +@kotlin.Metadata +@kotlin.coroutines.jvm.internal.DebugMetadata +final class InlineWithoutStateMachine_irKt$$complexSuspend$Continuation { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field label: int + @org.jetbrains.annotations.NotNull field result: java.lang.Object + public method (@org.jetbrains.annotations.Nullable p0: 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 +@kotlin.coroutines.jvm.internal.DebugMetadata +final class InlineWithoutStateMachine_irKt$$suspendHere$Continuation { + field L$0: java.lang.Object + field L$1: java.lang.Object + field L$2: java.lang.Object + field label: int + @org.jetbrains.annotations.NotNull field result: java.lang.Object + public method (@org.jetbrains.annotations.Nullable p0: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object +} + +@kotlin.coroutines.jvm.internal.DebugMetadata +@kotlin.Metadata +final class InlineWithoutStateMachine_irKt$box$1 { + private field $result: kotlin.jvm.internal.Ref$ObjectRef + field L$0: java.lang.Object + field L$1: java.lang.Object + private field label: int + inner class InlineWithoutStateMachine_irKt$box$1 + public method (@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef): void + public method (@org.jetbrains.annotations.NotNull p0: kotlin.jvm.internal.Ref$ObjectRef, @org.jetbrains.annotations.Nullable p1: kotlin.coroutines.Continuation): void + public final @org.jetbrains.annotations.NotNull method create(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): kotlin.coroutines.Continuation + public final method invoke(p0: java.lang.Object): java.lang.Object + public final @org.jetbrains.annotations.Nullable method invokeSuspend(@org.jetbrains.annotations.NotNull p0: java.lang.Object): java.lang.Object +} + +@kotlin.Metadata +public final class InlineWithoutStateMachine_irKt { + inner class InlineWithoutStateMachine_irKt$box$1 + public final static @org.jetbrains.annotations.NotNull method box(): java.lang.String + public final static method builder(@org.jetbrains.annotations.NotNull p0: kotlin.jvm.functions.Function1): void + public final static @org.jetbrains.annotations.Nullable method complexSuspend(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendHere(@org.jetbrains.annotations.NotNull p0: kotlin.coroutines.Continuation): java.lang.Object + public final static @org.jetbrains.annotations.Nullable method suspendThere(@org.jetbrains.annotations.NotNull p0: java.lang.String, @org.jetbrains.annotations.NotNull p1: kotlin.coroutines.Continuation): java.lang.Object +} diff --git a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatchTailCall.kt b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatchTailCall.kt index 3b98f16441e..da86c7c294b 100644 --- a/compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatchTailCall.kt +++ b/compiler/testData/codegen/box/coroutines/tailCallOptimizations/tryCatchTailCall.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR // TARGET_BACKEND: JVM // FULL_JDK // WITH_RUNTIME diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 80c1a3f8009..ce565c9f98c 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -8339,6 +8339,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.kt", "kotlin.coroutines"); } + @TestMetadata("inlineWithoutStateMachine_ir.kt") + public void testInlineWithoutStateMachine_ir() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt"); + } + @TestMetadata("innerObjectRetransformation.kt") public void testInnerObjectRetransformation_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines.experimental"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 696d0661c01..ae389773767 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -8297,6 +8297,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) public static class TailCallOptimizations extends AbstractLightAnalysisModeTest { + @TestMetadata("inlineWithoutStateMachine_ir.kt") + public void ignoreInlineWithoutStateMachine_ir() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt"); + } + private void runTest(String testDataFilePath) throws Exception { KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); } diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index 4c5b2cb4e9a..7ee37cf1eba 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -7289,6 +7289,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.kt", "kotlin.coroutines"); } + @TestMetadata("inlineWithoutStateMachine_ir.kt") + public void testInlineWithoutStateMachine_ir() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt"); + } + @TestMetadata("innerObjectRetransformation.kt") public void testInnerObjectRetransformation_1_3() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 6965559efe7..79839c7bbf1 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -6219,6 +6219,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.kt", "kotlin.coroutines"); } + @TestMetadata("inlineWithoutStateMachine_ir.kt") + public void testInlineWithoutStateMachine_ir() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt"); + } + @TestMetadata("innerObjectRetransformation.kt") public void testInnerObjectRetransformation_1_3() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index c243f192d2d..7cc64d34b1c 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -7239,6 +7239,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine.kt", "kotlin.coroutines"); } + @TestMetadata("inlineWithoutStateMachine_ir.kt") + public void testInlineWithoutStateMachine_ir() throws Exception { + runTest("compiler/testData/codegen/box/coroutines/tailCallOptimizations/inlineWithoutStateMachine_ir.kt"); + } + @TestMetadata("innerObjectRetransformation.kt") public void testInnerObjectRetransformation_1_2() throws Exception { runTestWithPackageReplacement("compiler/testData/codegen/box/coroutines/tailCallOptimizations/innerObjectRetransformation.kt", "kotlin.coroutines.experimental");