Do not just merge consequent LVT ranges, but also extend them

This commit is contained in:
Ilmir Usmanov
2021-08-12 01:29:10 +02:00
committed by TeamCityServer
parent 8d764fa50e
commit ebb340fe68
7 changed files with 58 additions and 37 deletions
@@ -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<VariableLivenessFrame
* This means, that function parameters do not longer span the whole function, including `this`.
* This might and will break some bytecode processors, including old versions of R8. See KT-24510.
*/
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean) {
private fun updateLvtAccordingToLiveness(method: MethodNode, isForNamedFunction: Boolean, suspensionPoints: List<LabelNode>) {
val liveness = analyzeLiveness(method)
fun List<LocalVariableNode>.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<LabelNode>,
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
}
@@ -15,6 +15,11 @@ fun main(args: Array<String>) {
suspend fun SequenceScope<Int>.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
@@ -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
@@ -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
@@ -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
+1 -1
View File
@@ -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
@@ -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: