JVM_IR: refine the condition for caching method nodes

Don't cache non-inline `suspend fun`s if there is no `$$forInline`;
do cache `invokeSuspend` if there *is* `invokeSuspend$$forInline`.
This commit is contained in:
pyos
2020-10-14 12:18:57 +02:00
committed by max-kammerer
parent d6e0d2a55b
commit 69127445a3
4 changed files with 16 additions and 14 deletions
@@ -312,10 +312,12 @@ class ClassCodegen private constructor(
private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>() private val generatedInlineMethods = mutableMapOf<IrFunction, SMAPAndMethodNode>()
fun generateMethodNode(method: IrFunction): SMAPAndMethodNode { fun generateMethodNode(method: IrFunction): SMAPAndMethodNode {
if (!method.isInline && !method.isSuspend) { if (!method.isInline && !method.isSuspendCapturingCrossinline()) {
// Inline methods can be used multiple times by `IrSourceCompilerForInline`, suspend methods // Inline methods can be used multiple times by `IrSourceCompilerForInline`, suspend methods
// could be used twice if they capture crossinline lambdas, and everything else is only // are used twice (`f` and `f$$forInline`) if they capture crossinline lambdas, and everything
// generated by `generateMethod` below so does not need caching. // else is only generated by `generateMethod` below so does not need caching.
// TODO: inline lambdas are not marked `isInline`, and are generally used once, but may be needed
// multiple times if declared in a `finally` block - should they be cached?
return FunctionCodegen(method, this).generate() return FunctionCodegen(method, this).generate()
} }
val (node, smap) = generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() } val (node, smap) = generatedInlineMethods.getOrPut(method) { FunctionCodegen(method, this).generate() }
@@ -348,14 +350,13 @@ class ClassCodegen private constructor(
override fun visitLineNumber(line: Int, start: Label) = override fun visitLineNumber(line: Int, start: Label) =
super.visitLineNumber(smapCopier.mapLineNumber(line), start) super.visitLineNumber(smapCopier.mapLineNumber(line), start)
} }
if (method.hasContinuation() || method.isInvokeSuspendOfLambda()) { if (method.hasContinuation()) {
// Generate a state machine within this method. The continuation class for it should be generated // Generate a state machine within this method. The continuation class for it should be generated
// lazily so that if tail call optimization kicks in, the unused class will not be written to the output. // lazily so that if tail call optimization kicks in, the unused class will not be written to the output.
val continuationClassCodegen = lazy { getOrCreate(method.continuationClass()!!, context, method) } val continuationClass = method.continuationClass() // null if `SuspendLambda.invokeSuspend` - `this` is continuation itself
node.acceptWithStateMachine(method, this, smapCopyingVisitor) { val continuationClassCodegen = lazy { if (continuationClass != null) getOrCreate(continuationClass, context, method) else this }
if (method.isSuspend) continuationClassCodegen.value.visitor else visitor node.acceptWithStateMachine(method, this, smapCopyingVisitor) { continuationClassCodegen.value.visitor }
} if (continuationClass != null && (continuationClassCodegen.isInitialized() || method.isSuspendCapturingCrossinline())) {
if (continuationClassCodegen.isInitialized() || method.alwaysNeedsContinuation()) {
continuationClassCodegen.value.generate() continuationClassCodegen.value.generate()
} }
} else { } else {
@@ -100,7 +100,7 @@ internal fun IrFunction.suspendForInlineToOriginal(): IrSimpleFunction? {
} as IrSimpleFunction? } as IrSimpleFunction?
} }
internal fun IrFunction.alwaysNeedsContinuation(): Boolean = internal fun IrFunction.isSuspendCapturingCrossinline(): Boolean =
this is IrSimpleFunction && hasContinuation() && parentAsClass.declarations.any { this is IrSimpleFunction && hasContinuation() && parentAsClass.declarations.any {
it is IrSimpleFunction && it.attributeOwnerId == attributeOwnerId && it is IrSimpleFunction && it.attributeOwnerId == attributeOwnerId &&
it.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE it.origin == JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
@@ -157,10 +157,10 @@ internal fun IrFunction.shouldContainSuspendMarkers(): Boolean = !isInvokeSuspen
!isBridgeToSuspendImplMethod() && !isBridgeToSuspendImplMethod() &&
!isStaticInlineClassReplacementDelegatingCall() !isStaticInlineClassReplacementDelegatingCall()
internal fun IrFunction.hasContinuation(): Boolean = isSuspend && shouldContainSuspendMarkers() && internal fun IrFunction.hasContinuation(): Boolean = isInvokeSuspendOfLambda() ||
// This is inline-only function isSuspend && shouldContainSuspendMarkers() &&
// These are templates for the inliner; the continuation is borrowed from the caller method.
!isEffectivelyInlineOnly() && !isEffectivelyInlineOnly() &&
// These are templates for the inliner; the continuation will be generated after it runs.
origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA && origin != IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA &&
origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE && origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE &&
origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE origin != JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
@@ -248,6 +248,7 @@ class ExpressionCodegen(
} }
private fun generateFakeContinuationConstructorIfNeeded() { private fun generateFakeContinuationConstructorIfNeeded() {
if (!irFunction.isSuspendCapturingCrossinline()) return
val continuationClass = irFunction.continuationClass() ?: return val continuationClass = irFunction.continuationClass() ?: return
val continuationType = typeMapper.mapClass(continuationClass) val continuationType = typeMapper.mapClass(continuationClass)
val continuationIndex = frameMap.getIndex(irFunction.continuationParameter()!!.symbol) val continuationIndex = frameMap.getIndex(irFunction.continuationParameter()!!.symbol)
@@ -214,7 +214,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX, INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX,
context.irBuiltIns.anyNType, context.irBuiltIns.anyNType,
Modality.FINAL, Modality.FINAL,
origin = JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE origin = JvmLoweredDeclarationOrigin.FOR_INLINE_STATE_MACHINE_TEMPLATE_CAPTURES_CROSSINLINE
).apply { ).apply {
copyAttributes(invokeSuspend) copyAttributes(invokeSuspend)
generateErrorForInlineBody() generateErrorForInlineBody()