[IR] Align debugging of suspend lambdas with old BE

The existing backend restores LVs and parameters from the suspend lambda
fields used for spilling between suspension points, hence they are
visible in the debugger as local variables, plain and simple.

This PR introduces the same pattern to the IR backend, to bring the
debugging experience in line with the existing backend.

Both backends are still at the mercy of the liveness analysis
performed in the coroutine transformer where a liveness analysis
minimizes live ranges of entries in the LVT. E.g. an unused parameter
will be dropped entirely.

Adjusted existing test expectations accounting for the differences in
LV behavior.
This commit is contained in:
Kristoffer Andersen
2020-11-24 12:53:16 +01:00
committed by Alexander Udalov
parent 2be62c13b0
commit 8a5f260d04
12 changed files with 112 additions and 78 deletions
@@ -5,12 +5,10 @@
package org.jetbrains.kotlin.backend.jvm.lower
import com.intellij.lang.jvm.source.JvmDeclarationSearch
import org.jetbrains.kotlin.backend.common.FileLoweringPass
import org.jetbrains.kotlin.backend.common.IrElementTransformerVoidWithContext
import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.backend.common.ir.isSuspend
import org.jetbrains.kotlin.backend.common.ir.moveBodyTo
import org.jetbrains.kotlin.backend.common.ir.*
import org.jetbrains.kotlin.backend.common.lower.LocalDeclarationsLowering
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase
@@ -29,12 +27,12 @@ import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.*
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrVariableImpl
import org.jetbrains.kotlin.ir.descriptors.WrappedVariableDescriptor
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrErrorExpressionImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrExpressionBodyImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrGetValueImpl
import org.jetbrains.kotlin.ir.expressions.impl.*
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrVariableSymbolImpl
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -43,6 +41,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.load.java.JavaDescriptorVisibilities
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.SpecialNames
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
internal val suspendLambdaPhase = makeIrFilePhase(
::SuspendLambdaLowering,
@@ -191,7 +190,7 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
}
val invokeSuspend = addInvokeSuspendForLambda(function, suspendLambda, parametersFields)
if (function.capturesCrossinline()) {
addInvokeSuspendForInlineForLambda(invokeSuspend)
addInvokeSuspendForInlineLambda(invokeSuspend)
}
if (createToOverride != null) {
addInvokeCallingCreate(addCreate(constructor, createToOverride, parametersFields), invokeSuspend, invokeToOverride)
@@ -209,19 +208,37 @@ private class SuspendLambdaLowering(context: JvmBackendContext) : SuspendLowerin
it.valueParameters[0].type.isKotlinResult()
}
return addFunctionOverride(superMethod, irFunction.startOffset, irFunction.endOffset).apply {
body = irFunction.moveBodyTo(this, mapOf())?.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
?: return expression
val field = fields[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
val localVals: List<IrVariable> = fields.mapIndexed { index, field ->
buildVariable(
parent = this,
startOffset = UNDEFINED_OFFSET,
endOffset = UNDEFINED_OFFSET,
origin = IrDeclarationOrigin.DEFINED,
name = field.name,
type = field.type
).apply {
val receiver = IrGetValueImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, dispatchReceiverParameter!!.symbol)
return IrGetFieldImpl(expression.startOffset, expression.endOffset, field.symbol, field.type, receiver)
val initializerBlock = IrBlockImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.type)
initializerBlock.statements += IrGetFieldImpl(UNDEFINED_OFFSET, UNDEFINED_OFFSET, field.symbol, field.type, receiver)
initializer = initializerBlock
}
}, null)
}
body = irFunction.moveBodyTo(this, mapOf())?.let { body ->
body.transform(object : IrElementTransformerVoid() {
override fun visitGetValue(expression: IrGetValue): IrExpression {
val parameter = (expression.symbol.owner as? IrValueParameter)?.takeIf { it.parent == irFunction }
?: return expression
val lvar = localVals[parameter.index + if (irFunction.extensionReceiverParameter != null) 1 else 0]
return IrGetValueImpl(expression.startOffset, expression.endOffset, lvar.symbol)
}
}, null)
context.irFactory.createBlockBody(UNDEFINED_OFFSET, UNDEFINED_OFFSET, localVals + body.statements)
}
}
}
private fun IrClass.addInvokeSuspendForInlineForLambda(invokeSuspend: IrSimpleFunction): IrSimpleFunction {
private fun IrClass.addInvokeSuspendForInlineLambda(invokeSuspend: IrSimpleFunction): IrSimpleFunction {
return addFunction(
INVOKE_SUSPEND_METHOD_NAME + FOR_INLINE_SUFFIX,
context.irBuiltIns.anyNType,