[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 protected abstract fun newFrame(nLocals: Int, nStack: Int): F
@Suppress("UNCHECKED_CAST") @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>?> { fun analyze(): Array<Frame<V>?> {
if (nInsns == 0) return frames 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>>( internal open class FastStackAnalyzer<V : Value, F : Frame<V>>(
owner: String, owner: String,
method: MethodNode, 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) { ) : FastAnalyzer<V, F>(owner, method, interpreter, pruneExceptionEdges = false) {
@Suppress("UNCHECKED_CAST") @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. // 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 override fun useFastComputeExceptionHandlers(): Boolean = true
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization.fixStack package org.jetbrains.kotlin.codegen.optimization.fixStack
import com.intellij.util.containers.Stack 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.isAfterInlineMarker
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
@@ -34,26 +35,40 @@ internal class FixStackAnalyzer(
owner: String, owner: String,
method: MethodNode, method: MethodNode,
val context: FixStackContext, val context: FixStackContext,
private val skipBreakContinueGotoEdges: Boolean = true private val skipBreakContinueGotoEdges: Boolean
) : FastStackAnalyzer<FixStackValue, FixStackAnalyzer.FixStackFrame>(owner, method, FixStackInterpreter()) { ) {
companion object { companion object {
// Stack size is always non-negative // Stack size is always non-negative
const val DEAD_CODE_STACK_SIZE = -1 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>>() private val loopEntryPointMarkers = hashMapOf<LabelNode, SmartList<AbstractInsnNode>>()
var maxExtraStackSize = 0; private set var maxExtraStackSize = 0; private set
private val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>() private val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>()
fun analyze() {
recordLoopEntryPointMarkers()
analyzer.analyze()
}
fun getStackToSpill(location: AbstractInsnNode): List<FixStackValue>? = fun getStackToSpill(location: AbstractInsnNode): List<FixStackValue>? =
spilledStacks[location] spilledStacks[location]
fun getActualStack(location: AbstractInsnNode): List<FixStackValue>? = fun getActualStack(location: AbstractInsnNode): List<FixStackValue>? =
getFrame(location)?.getStackContent() analyzer.getFrame(location)?.getStackContent()
fun getActualStackSize(location: AbstractInsnNode): Int = 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 { fun getExpectedStackSize(location: AbstractInsnNode): Int {
// We should look for expected stack size at loop entry point markers if available, // 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 // Find 1st live node among expected stack size nodes and return corresponding stack size
for (node in expectedStackSizeNodes) { for (node in expectedStackSizeNodes) {
val frame = getFrame(node) ?: continue val frame = analyzer.getFrame(node) ?: continue
return frame.stackSizeWithExtra return frame.stackSizeWithExtra
} }
@@ -71,10 +86,6 @@ internal class FixStackAnalyzer(
return DEAD_CODE_STACK_SIZE return DEAD_CODE_STACK_SIZE
} }
override fun beforeAnalyze() {
recordLoopEntryPointMarkers()
}
private fun recordLoopEntryPointMarkers() { private fun recordLoopEntryPointMarkers() {
// NB JVM_IR can generate nested loops with same exit labels (see kt37370.kt) // NB JVM_IR can generate nested loops with same exit labels (see kt37370.kt)
for (marker in context.fakeAlwaysFalseIfeqMarkers) { 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) { inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
private val extraStack = Stack<FixStackValue>() 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) } values.forEach { push(it) }
} }
@@ -194,7 +197,7 @@ internal class FixStackAnalyzer(
private fun executeRestoreStackInTryCatch(insn: AbstractInsnNode) { private fun executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
val saveNode = context.saveStackMarkerForRestoreMarker[insn] val saveNode = context.saveStackMarkerForRestoreMarker[insn]
val savedValues = spilledStacks.getOrElse(saveNode!!) { 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) pushAll(savedValues)
} }
@@ -51,7 +51,7 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun analyzeAndTransformBreakContinueGotos(context: FixStackContext, internalClassName: String, methodNode: MethodNode) { 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() analyzer.analyze()
methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize methodNode.maxStack = methodNode.maxStack + analyzer.maxExtraStackSize