[JVM] Use composition instead of inheritance in FixStackAnalyzer

This way we will be able to pass `createFrame` argument
directly to `FastAnalyzer`.
This commit is contained in:
Ivan Kylchik
2023-09-07 12:47:54 +02:00
committed by Space Team
parent 2327800a95
commit cfe38470af
4 changed files with 27 additions and 23 deletions
@@ -35,7 +35,7 @@ abstract class FastAnalyzer<V : Value, F : Frame<V>>(
protected abstract fun newFrame(nLocals: Int, nStack: Int): F
@Suppress("UNCHECKED_CAST")
protected fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F
fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F
fun analyze(): Array<Frame<V>?> {
if (nInsns == 0) return frames
@@ -47,10 +47,11 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
internal open class FastStackAnalyzer<V : Value, F : Frame<V>>(
owner: String,
method: MethodNode,
interpreter: Interpreter<V>
interpreter: Interpreter<V>,
private val createFrame: (Int, Int) -> Frame<V> = { nLocals, nStack -> Frame<V>(nLocals, nStack) }
) : FastAnalyzer<V, F>(owner, method, interpreter, pruneExceptionEdges = false) {
@Suppress("UNCHECKED_CAST")
override fun newFrame(nLocals: Int, nStack: Int): F = Frame<V>(nLocals, nStack) as F
override fun newFrame(nLocals: Int, nStack: Int): F = createFrame(nLocals, nStack) as F
// Don't have to visit the same exception handler multiple times - we care only about stack state at TCB start.
override fun useFastComputeExceptionHandlers(): Boolean = true
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization.fixStack
import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
@@ -34,26 +35,40 @@ internal class FixStackAnalyzer(
owner: String,
method: MethodNode,
val context: FixStackContext,
private val skipBreakContinueGotoEdges: Boolean = true
) : FastStackAnalyzer<FixStackValue, FixStackAnalyzer.FixStackFrame>(owner, method, FixStackInterpreter()) {
private val skipBreakContinueGotoEdges: Boolean
) {
companion object {
// Stack size is always non-negative
const val DEAD_CODE_STACK_SIZE = -1
}
private val analyzer = object : FastStackAnalyzer<FixStackValue, FixStackAnalyzer.FixStackFrame>(
owner, method, FixStackInterpreter(), { nLocals, nStack -> FixStackFrame(nLocals, nStack) }
) {
override fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean {
if (!skipBreakContinueGotoEdges) return true
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
}
}
private val loopEntryPointMarkers = hashMapOf<LabelNode, SmartList<AbstractInsnNode>>()
var maxExtraStackSize = 0; private set
private val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>()
fun analyze() {
recordLoopEntryPointMarkers()
analyzer.analyze()
}
fun getStackToSpill(location: AbstractInsnNode): List<FixStackValue>? =
spilledStacks[location]
fun getActualStack(location: AbstractInsnNode): List<FixStackValue>? =
getFrame(location)?.getStackContent()
analyzer.getFrame(location)?.getStackContent()
fun getActualStackSize(location: AbstractInsnNode): Int =
getFrame(location)?.stackSizeWithExtra ?: DEAD_CODE_STACK_SIZE
analyzer.getFrame(location)?.stackSizeWithExtra ?: DEAD_CODE_STACK_SIZE
fun getExpectedStackSize(location: AbstractInsnNode): Int {
// We should look for expected stack size at loop entry point markers if available,
@@ -62,7 +77,7 @@ internal class FixStackAnalyzer(
// Find 1st live node among expected stack size nodes and return corresponding stack size
for (node in expectedStackSizeNodes) {
val frame = getFrame(node) ?: continue
val frame = analyzer.getFrame(node) ?: continue
return frame.stackSizeWithExtra
}
@@ -71,10 +86,6 @@ internal class FixStackAnalyzer(
return DEAD_CODE_STACK_SIZE
}
override fun beforeAnalyze() {
recordLoopEntryPointMarkers()
}
private fun recordLoopEntryPointMarkers() {
// NB JVM_IR can generate nested loops with same exit labels (see kt37370.kt)
for (marker in context.fakeAlwaysFalseIfeqMarkers) {
@@ -85,14 +96,6 @@ internal class FixStackAnalyzer(
}
}
override fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean {
if (!skipBreakContinueGotoEdges) return true
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
}
override fun newFrame(nLocals: Int, nStack: Int): FixStackFrame =
FixStackFrame(nLocals, nStack)
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
private val extraStack = Stack<FixStackValue>()
@@ -144,7 +147,7 @@ internal class FixStackAnalyzer(
}
}
fun pushAll(values: Collection<FixStackValue>) {
private fun pushAll(values: Collection<FixStackValue>) {
values.forEach { push(it) }
}
@@ -194,7 +197,7 @@ internal class FixStackAnalyzer(
private fun executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
val savedValues = spilledStacks.getOrElse(saveNode!!) {
throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}")
throw AssertionError("${insn.insnText}: Restore stack is unavailable for ${saveNode.insnText}")
}
pushAll(savedValues)
}
@@ -51,7 +51,7 @@ class FixStackMethodTransformer : MethodTransformer() {
}
private fun analyzeAndTransformBreakContinueGotos(context: FixStackContext, internalClassName: String, methodNode: MethodNode) {
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context)
val analyzer = FixStackAnalyzer(internalClassName, methodNode, context, skipBreakContinueGotoEdges = true)
analyzer.analyze()
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize