JVM: erase conditional suspension markers from inline-only functions
Otherwise, the generated bytecode is unnecessarily suboptimal in some (arguably weird) cases. In the JVM backend, this was an accidental regression in #3260, as I had not noticed that effectively inline-only functions were handled by a separate branch in FunctionCodegen. In JVM_IR, I'm pretty sure the redundant markers have always been there as `isSuspensionPoint` in ExpressionCodegen never checked for effectively-inline-only-ness.
This commit is contained in:
@@ -80,7 +80,6 @@ import static org.jetbrains.kotlin.descriptors.ModalityKt.isOverridable;
|
|||||||
import static org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER;
|
import static org.jetbrains.kotlin.load.java.JvmAbi.LOCAL_VARIABLE_INLINE_ARGUMENT_SYNTHETIC_LINE_NUMBER;
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
|
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.getSourceFromDescriptor;
|
||||||
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
|
||||||
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isEffectivelyInlineOnly;
|
|
||||||
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
|
import static org.jetbrains.kotlin.resolve.inline.InlineOnlyKt.isInlineOnlyPrivateInBytecode;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
|
import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
|
||||||
import static org.jetbrains.kotlin.resolve.jvm.InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters;
|
import static org.jetbrains.kotlin.resolve.jvm.InlineClassManglingRulesKt.shouldHideConstructorDueToInlineClassTypeValueParameters;
|
||||||
@@ -124,18 +123,14 @@ public class FunctionCodegen {
|
|||||||
if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
|
if (owner.getContextKind() != OwnerKind.DEFAULT_IMPLS || function.hasBody()) {
|
||||||
FunctionGenerationStrategy strategy;
|
FunctionGenerationStrategy strategy;
|
||||||
if (functionDescriptor.isSuspend()) {
|
if (functionDescriptor.isSuspend()) {
|
||||||
if (isEffectivelyInlineOnly(functionDescriptor)) {
|
strategy = new SuspendFunctionGenerationStrategy(
|
||||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
|
state,
|
||||||
} else {
|
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
||||||
strategy = new SuspendFunctionGenerationStrategy(
|
function,
|
||||||
state,
|
v.getThisName(),
|
||||||
CoroutineCodegenUtilKt.<FunctionDescriptor>unwrapInitialDescriptorForSuspendFunction(functionDescriptor),
|
state.getConstructorCallNormalizationMode(),
|
||||||
function,
|
this
|
||||||
v.getThisName(),
|
);
|
||||||
state.getConstructorCallNormalizationMode(),
|
|
||||||
this
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
|
strategy = new FunctionGenerationStrategy.FunctionDefault(state, function);
|
||||||
}
|
}
|
||||||
|
|||||||
+11
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
|||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.psi.KtFunction
|
import org.jetbrains.kotlin.psi.KtFunction
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
|
||||||
|
import org.jetbrains.kotlin.resolve.inline.isEffectivelyInlineOnly
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
import org.jetbrains.kotlin.resolve.jvm.diagnostics.OtherOrigin
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
@@ -56,6 +57,9 @@ class SuspendFunctionGenerationStrategy(
|
|||||||
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
|
override fun wrapMethodVisitor(mv: MethodVisitor, access: Int, name: String, desc: String): MethodVisitor {
|
||||||
if (access and Opcodes.ACC_ABSTRACT != 0) return mv
|
if (access and Opcodes.ACC_ABSTRACT != 0) return mv
|
||||||
|
|
||||||
|
if (originalSuspendDescriptor.isEffectivelyInlineOnly()) {
|
||||||
|
return SuspendForInlineOnlyMethodVisitor(mv, access, name, desc)
|
||||||
|
}
|
||||||
val stateMachineBuilder = createStateMachineBuilder(mv, access, name, desc)
|
val stateMachineBuilder = createStateMachineBuilder(mv, access, name, desc)
|
||||||
if (originalSuspendDescriptor.isInline) {
|
if (originalSuspendDescriptor.isInline) {
|
||||||
return SuspendForInlineCopyingMethodVisitor(stateMachineBuilder, access, name, desc, functionCodegen::newMethod, keepAccess = false)
|
return SuspendForInlineCopyingMethodVisitor(stateMachineBuilder, access, name, desc, functionCodegen::newMethod, keepAccess = false)
|
||||||
@@ -156,6 +160,13 @@ class SuspendFunctionGenerationStrategy(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private class SuspendForInlineOnlyMethodVisitor(delegate: MethodVisitor, access: Int, name: String, desc: String) :
|
||||||
|
TransformationMethodVisitor(delegate, access, name, desc, null, null) {
|
||||||
|
override fun performTransformations(methodNode: MethodNode) {
|
||||||
|
methodNode.preprocessSuspendMarkers(forInline = true, keepFakeContinuation = false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// For named suspend function we generate two methods:
|
// For named suspend function we generate two methods:
|
||||||
// 1) to use as noinline function, which have state machine
|
// 1) to use as noinline function, which have state machine
|
||||||
// 2) to use from inliner: private one without state machine
|
// 2) to use from inliner: private one without state machine
|
||||||
|
|||||||
+1
-1
@@ -355,7 +355,7 @@ class ClassCodegen private constructor(
|
|||||||
|
|
||||||
val (node, smap) = generateMethodNode(method)
|
val (node, smap) = generateMethodNode(method)
|
||||||
node.preprocessSuspendMarkers(
|
node.preprocessSuspendMarkers(
|
||||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE,
|
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE || method.isEffectivelyInlineOnly(),
|
||||||
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
method.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
|
||||||
)
|
)
|
||||||
val mv = with(node) { visitor.newMethod(method.OtherOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
val mv = with(node) { visitor.newMethod(method.OtherOrigin, access, name, desc, signature, exceptions.toTypedArray()) }
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// WITH_COROUTINES
|
||||||
|
// TREAT_AS_ONE_FILE
|
||||||
|
inline suspend fun <reified T> f(x: suspend () -> T) = x()
|
||||||
|
// 0 InlineMarker
|
||||||
@@ -1391,6 +1391,11 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt");
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("effectivelyInlineOnly.kt")
|
||||||
|
public void testEffectivelyInlineOnly() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/effectivelyInlineOnly.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nonLocalReturn.kt")
|
@TestMetadata("nonLocalReturn.kt")
|
||||||
public void testNonLocalReturn() throws Exception {
|
public void testNonLocalReturn() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt");
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt");
|
||||||
|
|||||||
+5
@@ -1401,6 +1401,11 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
|
|||||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt");
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/doNotReassignContinuation.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("effectivelyInlineOnly.kt")
|
||||||
|
public void testEffectivelyInlineOnly() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/effectivelyInlineOnly.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("nonLocalReturn.kt")
|
@TestMetadata("nonLocalReturn.kt")
|
||||||
public void testNonLocalReturn() throws Exception {
|
public void testNonLocalReturn() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt");
|
runTest("compiler/testData/codegen/bytecodeText/coroutines/nonLocalReturn.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user