Do not remove dead variables' LVT records
Also, extend liveness of alive variable to nearest suspension points #KT-44714
This commit is contained in:
+56
-5
@@ -1217,6 +1217,27 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
|||||||
fun isAlive(insnIndex: Int, variableIndex: Int): Boolean =
|
fun isAlive(insnIndex: Int, variableIndex: Int): Boolean =
|
||||||
liveness[insnIndex].isAlive(variableIndex)
|
liveness[insnIndex].isAlive(variableIndex)
|
||||||
|
|
||||||
|
fun nextSuspensionPointLabel(insn: AbstractInsnNode): LabelNode {
|
||||||
|
val suspensionPoint =
|
||||||
|
InsnSequence(insn, method.instructions.last).firstOrNull { isBeforeSuspendMarker(it) } ?: method.instructions.last
|
||||||
|
return suspensionPoint as? LabelNode ?: suspensionPoint.findPreviousOrNull { it is LabelNode } as LabelNode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun previousSuspensionPointLabel(insn: AbstractInsnNode): LabelNode {
|
||||||
|
val suspensionPoint =
|
||||||
|
InsnSequence(method.instructions.first, insn).lastOrNull { isAfterSuspendMarker(it) } ?: method.instructions.first
|
||||||
|
return suspensionPoint as? LabelNode ?: suspensionPoint.findNextOrNull { it is LabelNode } as LabelNode
|
||||||
|
}
|
||||||
|
|
||||||
|
fun min(a: LabelNode, b: LabelNode): LabelNode =
|
||||||
|
if (method.instructions.indexOf(a) < method.instructions.indexOf(b)) a else b
|
||||||
|
|
||||||
|
fun max(a: LabelNode, b: LabelNode): LabelNode =
|
||||||
|
if (method.instructions.indexOf(a) < method.instructions.indexOf(b)) b else a
|
||||||
|
|
||||||
|
fun containsSuspensionPoint(a: LabelNode, b: LabelNode): Boolean =
|
||||||
|
InsnSequence(min(a, b), max(a, b)).none { isBeforeSuspendMarker(it) }
|
||||||
|
|
||||||
val oldLvt = arrayListOf<LocalVariableNode>()
|
val oldLvt = arrayListOf<LocalVariableNode>()
|
||||||
for (record in method.localVariables) {
|
for (record in method.localVariables) {
|
||||||
oldLvt += record
|
oldLvt += record
|
||||||
@@ -1231,29 +1252,43 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
|||||||
for (insnIndex in 0 until (method.instructions.size() - 1)) {
|
for (insnIndex in 0 until (method.instructions.size() - 1)) {
|
||||||
val insn = method.instructions[insnIndex]
|
val insn = method.instructions[insnIndex]
|
||||||
if (!isAlive(insnIndex, variableIndex) && isAlive(insnIndex + 1, variableIndex)) {
|
if (!isAlive(insnIndex, variableIndex) && isAlive(insnIndex + 1, variableIndex)) {
|
||||||
startLabel = insn as? LabelNode ?: insn.findNextOrNull { it is LabelNode } as? LabelNode
|
val lvtRecord = oldLvt.findRecord(insnIndex, variableIndex) ?: continue
|
||||||
|
startLabel = max(lvtRecord.start, previousSuspensionPointLabel(insn))
|
||||||
}
|
}
|
||||||
if (isAlive(insnIndex, variableIndex) && !isAlive(insnIndex + 1, variableIndex)) {
|
if (isAlive(insnIndex, variableIndex) && !isAlive(insnIndex + 1, variableIndex)) {
|
||||||
// No variable in LVT -> do not add one
|
// No variable in LVT -> do not add one
|
||||||
val lvtRecord = oldLvt.findRecord(insnIndex, variableIndex) ?: continue
|
val lvtRecord = oldLvt.findRecord(insnIndex, variableIndex) ?: continue
|
||||||
if (lvtRecord.name == CONTINUATION_VARIABLE_NAME) continue
|
if (lvtRecord.name == CONTINUATION_VARIABLE_NAME) continue
|
||||||
val endLabel = insn as? LabelNode ?: insn.findNextOrNull { it is LabelNode } as? LabelNode ?: continue
|
// Extend lvt record to the next suspension point
|
||||||
|
val endLabel = min(lvtRecord.end, nextSuspensionPointLabel(insn))
|
||||||
// startLabel can be null in case of parameters
|
// startLabel can be null in case of parameters
|
||||||
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
@Suppress("NAME_SHADOWING") val startLabel = startLabel ?: lvtRecord.start
|
||||||
// Attempt to extend existing local variable node corresponding to the record in
|
// Attempt to extend existing local variable node corresponding to the record in
|
||||||
// the original local variable table.
|
// the original local variable table.
|
||||||
var recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
val recordToExtend: LocalVariableNode? = oldLvtNodeToLatestNewLvtNode[lvtRecord]
|
||||||
if (recordToExtend != null && InsnSequence(recordToExtend.end, startLabel).none { isBeforeSuspendMarker(it) }) {
|
if (recordToExtend != null && containsSuspensionPoint(recordToExtend.end, startLabel)) {
|
||||||
recordToExtend.end = endLabel
|
recordToExtend.end = endLabel
|
||||||
} else {
|
} else {
|
||||||
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
val node = LocalVariableNode(lvtRecord.name, lvtRecord.desc, lvtRecord.signature, startLabel, endLabel, lvtRecord.index)
|
||||||
method.localVariables.add(node)
|
if (lvtRecord !in oldLvtNodeToLatestNewLvtNode) {
|
||||||
|
method.localVariables.add(node)
|
||||||
|
}
|
||||||
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
oldLvtNodeToLatestNewLvtNode[lvtRecord] = node
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val deadVariables = arrayListOf<Int>()
|
||||||
|
outer@for (variableIndex in start until method.maxLocals) {
|
||||||
|
if (oldLvt.none { it.index == variableIndex }) continue
|
||||||
|
if (oldLvt.none { it.index == variableIndex }) continue
|
||||||
|
for (insnIndex in 0 until (method.instructions.size() - 1)) {
|
||||||
|
if (isAlive(insnIndex, variableIndex)) continue@outer
|
||||||
|
}
|
||||||
|
deadVariables += variableIndex
|
||||||
|
}
|
||||||
|
|
||||||
for (variable in oldLvt) {
|
for (variable in oldLvt) {
|
||||||
// $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
|
// $continuation and $result are dead, but they are used by debugger, as well as fake inliner variables
|
||||||
// For example, $continuation is used to create async stack trace
|
// For example, $continuation is used to create async stack trace
|
||||||
@@ -1262,10 +1297,26 @@ private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction:
|
|||||||
isFakeLocalVariableForInline(variable.name)
|
isFakeLocalVariableForInline(variable.name)
|
||||||
) {
|
) {
|
||||||
method.localVariables.add(variable)
|
method.localVariables.add(variable)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
// this acts like $continuation for lambdas. For example, it is used by debugger to create async stack trace. Keep it.
|
// this acts like $continuation for lambdas. For example, it is used by debugger to create async stack trace. Keep it.
|
||||||
if (variable.name == "this" && !isForNamedFunction) {
|
if (variable.name == "this" && !isForNamedFunction) {
|
||||||
method.localVariables.add(variable)
|
method.localVariables.add(variable)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shrink LVT records of dead variables to the next suspension point
|
||||||
|
if (variable.index in deadVariables) {
|
||||||
|
method.localVariables.add(
|
||||||
|
LocalVariableNode(
|
||||||
|
variable.name,
|
||||||
|
variable.desc,
|
||||||
|
variable.signature,
|
||||||
|
variable.start,
|
||||||
|
nextSuspensionPointLabel(variable.start),
|
||||||
|
variable.index
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
suspend fun dummy() {}
|
||||||
|
|
||||||
|
suspend fun test() {
|
||||||
|
dummy()
|
||||||
|
val a = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// METHOD : SuspendFunctionDeadVariablesKt.test(Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||||
|
|
||||||
|
// VARIABLE : NAME=a TYPE=I INDEX=1
|
||||||
|
// VARIABLE : NAME=$continuation TYPE=Lkotlin/coroutines/Continuation; INDEX=3
|
||||||
|
// VARIABLE : NAME=$result TYPE=Ljava/lang/Object; INDEX=2
|
||||||
+1
-1
@@ -15,7 +15,7 @@ fun main(args: Array<String>) {
|
|||||||
suspend fun SequenceScope<Int>.awaitSeq(): Int = 42
|
suspend fun SequenceScope<Int>.awaitSeq(): Int = 42
|
||||||
|
|
||||||
|
|
||||||
// 1 LOCALVARIABLE a I L[0-9]+ L18
|
// 1 LOCALVARIABLE a I L[0-9]+ L4
|
||||||
|
|
||||||
/* TODO: JVM_IR does not generate LINENUMBER at the end of the lambda */
|
/* TODO: JVM_IR does not generate LINENUMBER at the end of the lambda */
|
||||||
// JVM_TEMPLATES
|
// JVM_TEMPLATES
|
||||||
|
|||||||
+1
-1
@@ -36,6 +36,6 @@ suspend fun box() {
|
|||||||
// test.kt:5 foo: $completion:kotlin.coroutines.Continuation=A$foo1$1
|
// 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: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: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, dead:long=42:long
|
||||||
// test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
// test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
+1
-1
@@ -32,6 +32,6 @@ suspend fun box() {
|
|||||||
// test.kt:4 foo: $completion:kotlin.coroutines.Continuation=TestKt$foo1$1
|
// 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: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: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, dead:long=42:long
|
||||||
// test.kt:12 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:12 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
// test.kt:13 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:13 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
+1
-1
@@ -36,6 +36,6 @@ suspend fun box() {
|
|||||||
// test.kt:6 foo: $this$foo:A=A, $completion:kotlin.coroutines.Continuation=TestKt$foo1$1
|
// 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: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: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, dead:long=42:long
|
||||||
// test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:14 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
// test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
// test.kt:15 box: $completion:kotlin.coroutines.Continuation=helpers.ResultContinuation
|
||||||
+4
-4
@@ -31,19 +31,19 @@ suspend fun box() {
|
|||||||
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:5 f: x:int=0:int
|
// test.kt:5 f: x:int=0:int
|
||||||
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:10 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:10 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:5 f: x:int=1:int
|
// test.kt:5 f: x:int=1:int
|
||||||
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
// test.kt:11 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:10 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:10 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:14 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:14 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
||||||
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
||||||
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:5 f: x:int=0:int
|
// test.kt:5 f: x:int=0:int
|
||||||
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=0:int
|
||||||
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:5 f: x:int=1:int
|
// test.kt:5 f: x:int=1:int
|
||||||
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
// test.kt:16 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:15 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null, x:int=1:int
|
||||||
// test.kt:18 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
// test.kt:18 box: $continuation:kotlin.coroutines.Continuation=TestKt$box$1, $result:java.lang.Object=null
|
||||||
|
|||||||
@@ -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
|
// 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
|
// 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
|
// LOCAL VARIABLES JVM
|
||||||
// test.kt:-1 invoke:
|
// test.kt:-1 invoke:
|
||||||
|
|||||||
Generated
+5
@@ -80,6 +80,11 @@ public class CheckLocalVariablesTableTestGenerated extends AbstractCheckLocalVar
|
|||||||
runTest("compiler/testData/checkLocalVariablesTable/objectInLocalPropertyDelegate.kt");
|
runTest("compiler/testData/checkLocalVariablesTable/objectInLocalPropertyDelegate.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendFunctionDeadVariables.kt")
|
||||||
|
public void testSuspendFunctionDeadVariables() throws Exception {
|
||||||
|
runTest("compiler/testData/checkLocalVariablesTable/suspendFunctionDeadVariables.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("underscoreNames.kt")
|
@TestMetadata("underscoreNames.kt")
|
||||||
public void testUnderscoreNames() throws Exception {
|
public void testUnderscoreNames() throws Exception {
|
||||||
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
||||||
|
|||||||
Generated
+5
@@ -80,6 +80,11 @@ public class IrCheckLocalVariablesTableTestGenerated extends AbstractIrCheckLoca
|
|||||||
runTest("compiler/testData/checkLocalVariablesTable/objectInLocalPropertyDelegate.kt");
|
runTest("compiler/testData/checkLocalVariablesTable/objectInLocalPropertyDelegate.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("suspendFunctionDeadVariables.kt")
|
||||||
|
public void testSuspendFunctionDeadVariables() throws Exception {
|
||||||
|
runTest("compiler/testData/checkLocalVariablesTable/suspendFunctionDeadVariables.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("underscoreNames.kt")
|
@TestMetadata("underscoreNames.kt")
|
||||||
public void testUnderscoreNames() throws Exception {
|
public void testUnderscoreNames() throws Exception {
|
||||||
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
runTest("compiler/testData/checkLocalVariablesTable/underscoreNames.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user