From 571971c5dba6fc7fba3b9d2a248b42e05fa327f3 Mon Sep 17 00:00:00 2001 From: Mads Ager Date: Mon, 22 Mar 2021 12:19:56 +0100 Subject: [PATCH] JVM: Fix local variable ranges in state machine transform. When a suspension point is inlined, the inlining local spans unspilling code where the local slot has not been initialized. The transformer already inserted initialization code for the local, however, it did not split the local variable table. Therefore, the inlining local is defined on instructions where the local slot has no value (namely the instructions that initialize the local slot on the unspilling path). This change adds splitting of the local variable table as well. When updating to a new version of D8, the uninitialized local slot showed up. D8 will repair it in this case, but it is better to not generate such code. --- .../CoroutineTransformerMethodVisitor.kt | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index 64e81092a28..e1c8428bab8 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -201,20 +201,38 @@ class CoroutineTransformerMethodVisitor( // When suspension point is inlined, it is in range of fake inliner variables. // Path from TABLESWITCH into unspilling goes to latter part of the range. - // In this case the variables are uninitialized, initialize them + // In this case the variables are uninitialized, initialize them, and split the local variable + // range so that the local variable is only defined when initialized. private fun initializeFakeInlinerVariables(methodNode: MethodNode, stateLabels: List) { for (stateLabel in stateLabels) { + val newRecords = mutableListOf() for (record in methodNode.localVariables) { if (isFakeLocalVariableForInline(record.name) && methodNode.instructions.indexOf(record.start) < methodNode.instructions.indexOf(stateLabel) && methodNode.instructions.indexOf(stateLabel) < methodNode.instructions.indexOf(record.end) ) { + val newEnd = record.end + val newStart = LabelNode() + record.end = stateLabel methodNode.instructions.insert(stateLabel, withInstructionAdapter { iconst(0) store(record.index, Type.INT_TYPE) + }.also { + it.add(newStart) }) + newRecords.add( + LocalVariableNode( + record.name, + record.desc, + record.signature, + newStart, + newEnd, + record.index + ) + ) } } + methodNode.localVariables.addAll(newRecords) } }