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:
+10
-9
@@ -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 {
|
||||||
|
|||||||
+4
-4
@@ -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
|
||||||
|
|||||||
+1
@@ -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)
|
||||||
|
|||||||
+1
-1
@@ -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()
|
||||||
|
|||||||
Reference in New Issue
Block a user