Cleanup spilled variables
#KT-16222
This commit is contained in:
+56
-3
@@ -608,8 +608,8 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
// NB: it's also rather useful for sake of optimization
|
// NB: it's also rather useful for sake of optimization
|
||||||
val livenessFrame = livenessFrames[suspensionCallBegin.index()]
|
val livenessFrame = livenessFrames[suspensionCallBegin.index()]
|
||||||
|
|
||||||
val referencesToSpill = arrayListOf<Pair<Int, SpillableVariable?>>()
|
val referencesToSpill = arrayListOf<ReferenceToSpill>()
|
||||||
val primitivesToSpill = arrayListOf<Pair<Int, SpillableVariable>>()
|
val primitivesToSpill = arrayListOf<PrimitiveToSpill>()
|
||||||
|
|
||||||
// 0 - this
|
// 0 - this
|
||||||
// 1 - parameter
|
// 1 - parameter
|
||||||
@@ -649,7 +649,40 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Calculate variable to cleaup
|
// Calculate variables to cleanup
|
||||||
|
|
||||||
|
// Use CFG to calculate amount of spilled variables in previous suspension point (P) and current one (C).
|
||||||
|
// All fields from L$C to L$P should be cleaned. I.e. we should spill ACONST_NULL to them.
|
||||||
|
val cfg = ControlFlowGraph.build(methodNode)
|
||||||
|
|
||||||
|
// Collect all immediately preceding suspension points. I.e. suspension points, from which there is a path
|
||||||
|
// into current one, that does not cross other suspension points.
|
||||||
|
val suspensionPointEnds = suspensionPoints.associateBy { it.suspensionCallEnd }
|
||||||
|
fun findSuspensionPointPredecessors(suspension: SuspensionPoint): List<SuspensionPoint> {
|
||||||
|
val visited = mutableSetOf<AbstractInsnNode>()
|
||||||
|
fun dfs(current: AbstractInsnNode): List<SuspensionPoint> {
|
||||||
|
if (!visited.add(current)) return emptyList()
|
||||||
|
suspensionPointEnds[current]?.let { return listOf(it) }
|
||||||
|
return cfg.getPredecessorsIndices(current).flatMap { dfs(instructions[it]) }
|
||||||
|
}
|
||||||
|
return dfs(suspension.suspensionCallBegin)
|
||||||
|
}
|
||||||
|
|
||||||
|
val predSuspensionPoints = suspensionPoints.associateWith { findSuspensionPointPredecessors(it) }
|
||||||
|
|
||||||
|
// Calculate all pairs SuspensionPoint -> C and P, where P is minimum of all preds' Cs
|
||||||
|
fun countVariablesToSpill(index: Int): Int =
|
||||||
|
referencesToSpillBySuspensionPointIndex[index].count { (_, variable) -> variable != null }
|
||||||
|
|
||||||
|
val referencesToCleanBySuspensionPointIndex = arrayListOf<Pair<Int, Int>>() // current to pred
|
||||||
|
for (suspensionPointIndex in suspensionPoints.indices) {
|
||||||
|
val suspensionPoint = suspensionPoints[suspensionPointIndex]
|
||||||
|
val currentSpilledReferencesCount = countVariablesToSpill(suspensionPointIndex)
|
||||||
|
val preds = predSuspensionPoints[suspensionPoint]
|
||||||
|
val predSpilledReferencesCount =
|
||||||
|
if (preds.isNullOrEmpty()) 0 else preds.maxOf { countVariablesToSpill(suspensionPoints.indexOf(it)) }
|
||||||
|
referencesToCleanBySuspensionPointIndex += currentSpilledReferencesCount to predSpilledReferencesCount
|
||||||
|
}
|
||||||
|
|
||||||
// Mutate method node
|
// Mutate method node
|
||||||
|
|
||||||
@@ -691,11 +724,31 @@ class CoroutineTransformerMethodVisitor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun cleanUpField(suspension: SuspensionPoint, fieldIndex: Int) {
|
||||||
|
with(instructions) {
|
||||||
|
insertBefore(suspension.suspensionCallBegin, withInstructionAdapter {
|
||||||
|
load(continuationIndex, AsmTypes.OBJECT_TYPE)
|
||||||
|
aconst(null)
|
||||||
|
putfield(
|
||||||
|
classBuilderForCoroutineState.thisName,
|
||||||
|
"L\$$fieldIndex",
|
||||||
|
AsmTypes.OBJECT_TYPE.descriptor
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (suspensionPointIndex in suspensionPoints.indices) {
|
for (suspensionPointIndex in suspensionPoints.indices) {
|
||||||
val suspension = suspensionPoints[suspensionPointIndex]
|
val suspension = suspensionPoints[suspensionPointIndex]
|
||||||
for ((slot, referenceToSpill) in referencesToSpillBySuspensionPointIndex[suspensionPointIndex]) {
|
for ((slot, referenceToSpill) in referencesToSpillBySuspensionPointIndex[suspensionPointIndex]) {
|
||||||
generateSpillAndUnspill(suspension, slot, referenceToSpill)
|
generateSpillAndUnspill(suspension, slot, referenceToSpill)
|
||||||
}
|
}
|
||||||
|
val (currentSpilledCount, predSpilledCount) = referencesToCleanBySuspensionPointIndex[suspensionPointIndex]
|
||||||
|
if (predSpilledCount > currentSpilledCount) {
|
||||||
|
for (fieldIndex in currentSpilledCount until predSpilledCount) {
|
||||||
|
cleanUpField(suspension, fieldIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
for ((slot, primitiveToSpill) in primitivesToSpillBySuspensionPointIndex[suspensionPointIndex]) {
|
for ((slot, primitiveToSpill) in primitivesToSpillBySuspensionPointIndex[suspensionPointIndex]) {
|
||||||
generateSpillAndUnspill(suspension, slot, primitiveToSpill)
|
generateSpillAndUnspill(suspension, slot, primitiveToSpill)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,5 +5,6 @@ suspend fun topLevel(a: String, b: String) {
|
|||||||
blackhole(b) // no spills
|
blackhole(b) // no spills
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1 PUTFIELD .*L\$0 : Ljava/lang/Object;
|
// a and a's cleanup
|
||||||
|
// 2 PUTFIELD .*L\$0 : Ljava/lang/Object;
|
||||||
// 0 PUTFIELD .*L\$1 : Ljava/lang/Object;
|
// 0 PUTFIELD .*L\$1 : Ljava/lang/Object;
|
||||||
Reference in New Issue
Block a user