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 0690a25a64d..7a2c0ced7f2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -192,7 +192,7 @@ class CoroutineTransformerMethodVisitor( dropUnboxInlineClassMarkers(methodNode, suspensionPoints) methodNode.removeEmptyCatchBlocks() - updateLvtAccordingToLiveness(methodNode, isForNamedFunction) + updateLvtAccordingToLiveness(methodNode, isForNamedFunction, stateLabels) if (languageVersionSettings.isReleaseCoroutines()) { writeDebugMetadata(methodNode, suspensionPointLineNumbers, spilledToVariableMapping) @@ -1269,7 +1269,7 @@ private fun MethodNode.nodeTextWithLiveness(liveness: List) { val liveness = analyzeLiveness(method) fun List.findRecord(insnIndex: Int, variableIndex: Int): LocalVariableNode? { @@ -1288,8 +1288,8 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: fun nextLabel(node: AbstractInsnNode?): LabelNode? { var current = node while (current != null) { - if (current is LabelNode) return current as LabelNode - current = current!!.next + if (current is LabelNode) return current + current = current.next } return null } @@ -1346,33 +1346,17 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: // Attempt to extend existing local variable node corresponding to the record in // the original local variable table, if there is no back-edge - val recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord] - var recordExtended = false - if (recordToExtend != null) { - var hasBackEdgeOrStore = false - var current: AbstractInsnNode? = recordToExtend.end - while (current != null && current != endLabel) { - if (current is JumpInsnNode) { - if (method.instructions.indexOf((current as JumpInsnNode).label) < method.instructions.indexOf(current)) { - hasBackEdgeOrStore = true - break - } - } - if (current!!.isStoreOperation() && (current as VarInsnNode).`var` == recordToExtend.index) { - hasBackEdgeOrStore = true - break - } - current = current!!.next - } - if (!hasBackEdgeOrStore) { - recordToExtend.end = endLabel - recordExtended = true - } - } - if (!recordExtended) { - val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) - method.localVariables.add(node) - oldLvtNodeToLatestNewLvtNode[lvtRecord] = node + val latest = oldLvtNodeToLatestNewLvtNode[lvtRecord] + // if we can extend the previous range to where the local variable dies, we do not need a + // new entry, we know we cannot extend it to the lvt.endOffset, if we could we would have + // done so when we added it below. + val extended = latest?.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end) ?: false + if (!extended) { + val new = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index) + oldLvtNodeToLatestNewLvtNode[lvtRecord] = new + method.localVariables.add(new) + // see if we can extend it all the way to the old end + new.extendRecordIfPossible(method, suspensionPoints, lvtRecord.end) } } } @@ -1395,3 +1379,35 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: } } } + +/* We cannot extend a record if there is STORE instruction or a back-edge. + * STORE instructions can signify a unspilling operation, in which case, the variable will become visible before it unspilled, + * back-edges occur in loops. + * + * @return true if the range has been extended + */ +private fun LocalVariableNode.extendRecordIfPossible( + method: MethodNode, + suspensionPoints: List, + endLabel: LabelNode +): Boolean { + val nextSuspensionPointLabel = suspensionPoints.find { it in InsnSequence(end, endLabel) } ?: endLabel + + var current: AbstractInsnNode? = end + while (current != null && current != nextSuspensionPointLabel) { + if (current is JumpInsnNode) { + if (method.instructions.indexOf(current.label) < method.instructions.indexOf(current)) { + return false + } + } + // TODO: HACK + // TODO: Find correct label, which is OK to be used as end label. + if (current.opcode == Opcodes.ARETURN && nextSuspensionPointLabel != endLabel) return false + if (current.isStoreOperation() && (current as VarInsnNode).`var` == index) { + return false + } + current = current.next + } + end = nextSuspensionPointLabel + return true +} diff --git a/compiler/testData/codegen/bytecodeText/coroutines/debug/localVariableCorrectLabel.kt b/compiler/testData/codegen/bytecodeText/coroutines/debug/localVariableCorrectLabel.kt index 5db330c7b38..28c410d1313 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/debug/localVariableCorrectLabel.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/debug/localVariableCorrectLabel.kt @@ -15,6 +15,11 @@ fun main(args: Array) { suspend fun SequenceScope.awaitSeq(): Int = 42 // 1 LINENUMBER 9 L18 -// 1 LOCALVARIABLE a I L[0-9]+ L17 + +// JVM_IR_TEMPLATES +// 1 LOCALVARIABLE a I L[0-9]+ L4 + +// JVM_TEMPLATES +// 1 LOCALVARIABLE a I L[0-9]+ L18 // IGNORE_BACKEND_FIR: JVM_IR diff --git a/compiler/testData/debug/localVariables/suspend/completion/nonStaticStateMachine.kt b/compiler/testData/debug/localVariables/suspend/completion/nonStaticStateMachine.kt index 7272d27e1dc..9b4c26f5fa7 100644 --- a/compiler/testData/debug/localVariables/suspend/completion/nonStaticStateMachine.kt +++ b/compiler/testData/debug/localVariables/suspend/completion/nonStaticStateMachine.kt @@ -36,6 +36,6 @@ suspend fun box() { // test.kt:5 foo: $completion:kotlin.coroutines.Continuation=A$foo1$1 // test.kt:8 foo1: $continuation:kotlin.coroutines.Continuation=A$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:9 foo1: $continuation:kotlin.coroutines.Continuation=A$foo1$1, $result:java.lang.Object=null, l:long=42:long -// test.kt:10 foo1: $continuation:kotlin.coroutines.Continuation=A$foo1$1, $result:java.lang.Object=null +// test.kt:10 foo1: $continuation:kotlin.coroutines.Continuation=A$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation // test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation diff --git a/compiler/testData/debug/localVariables/suspend/completion/staticStateMachine.kt b/compiler/testData/debug/localVariables/suspend/completion/staticStateMachine.kt index bc7b5d02ae5..4620d3efd81 100644 --- a/compiler/testData/debug/localVariables/suspend/completion/staticStateMachine.kt +++ b/compiler/testData/debug/localVariables/suspend/completion/staticStateMachine.kt @@ -32,6 +32,6 @@ suspend fun box() { // test.kt:4 foo: $completion:kotlin.coroutines.Continuation=TestKt$foo1$1 // test.kt:7 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:8 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long -// test.kt:9 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null +// test.kt:9 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:12 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation // test.kt:13 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation diff --git a/compiler/testData/debug/localVariables/suspend/completion/staticStateMachineReceiver.kt b/compiler/testData/debug/localVariables/suspend/completion/staticStateMachineReceiver.kt index 889777d59f9..14f8129e98d 100644 --- a/compiler/testData/debug/localVariables/suspend/completion/staticStateMachineReceiver.kt +++ b/compiler/testData/debug/localVariables/suspend/completion/staticStateMachineReceiver.kt @@ -36,6 +36,6 @@ suspend fun box() { // test.kt:6 foo: $this$foo:A=A, $completion:kotlin.coroutines.Continuation=TestKt$foo1$1 // test.kt:9 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:10 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long -// test.kt:11 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null +// test.kt:11 foo1: $continuation:kotlin.coroutines.Continuation=TestKt$foo1$1, $result:java.lang.Object=null, l:long=42:long // test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation // test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation diff --git a/compiler/testData/debug/localVariables/suspend/mergeLvt.kt b/compiler/testData/debug/localVariables/suspend/mergeLvt.kt index 6880f64639e..ab681a5af66 100644 --- a/compiler/testData/debug/localVariables/suspend/mergeLvt.kt +++ b/compiler/testData/debug/localVariables/suspend/mergeLvt.kt @@ -45,7 +45,7 @@ suspend fun box() { // test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $this$extensionFun$iv$iv:AtomicInt=AtomicInt, $i$f$extensionFun:int=0:int // test.kt:6 getValue: // test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $this$extensionFun$iv$iv:AtomicInt=AtomicInt, $i$f$extensionFun:int=0:int -// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $i$f$extensionFun:int=0:int +// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $this$extensionFun$iv$iv:AtomicInt=AtomicInt, $i$f$extensionFun:int=0:int // test.kt:20 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int // test.kt:21 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $i$a$-suspendCoroutineUninterceptedOrReturn-TestKt$suspendBar$2$iv:int=0:int // test.kt:22 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, $i$f$suspendBar:int=0:int, $i$a$-suspendCoroutineUninterceptedOrReturn-TestKt$suspendBar$2$iv:int=0:int diff --git a/compiler/testData/debug/localVariables/suspend/underscoreNames.kt b/compiler/testData/debug/localVariables/suspend/underscoreNames.kt index b5ceff8353d..87d41e12227 100644 --- a/compiler/testData/debug/localVariables/suspend/underscoreNames.kt +++ b/compiler/testData/debug/localVariables/suspend/underscoreNames.kt @@ -45,7 +45,7 @@ suspend fun box() = foo(A()) { (x_param, _, y_param) -> // test.kt:12 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A, x_param:java.lang.String="O":java.lang.String // LOCAL VARIABLES -// test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String +// test.kt:13 invokeSuspend: $result:java.lang.Object=kotlin.Unit, $dstr$x_param$_u24__u24$y_param:A=A, x_param:java.lang.String="O":java.lang.String, y_param:java.lang.String="K":java.lang.String // LOCAL VARIABLES JVM // test.kt:-1 invoke: