From cfe38470afcf9ec510249a068aedabd6a21867e8 Mon Sep 17 00:00:00 2001 From: Ivan Kylchik Date: Thu, 7 Sep 2023 12:47:54 +0200 Subject: [PATCH] [JVM] Use composition instead of inheritance in `FixStackAnalyzer` This way we will be able to pass `createFrame` argument directly to `FastAnalyzer`. --- .../optimization/common/FastAnalyzer.kt | 2 +- .../fixStack/FastStackAnalyzer.kt | 5 ++- .../optimization/fixStack/FixStackAnalyzer.kt | 41 ++++++++++--------- .../fixStack/FixStackMethodTransformer.kt | 2 +- 4 files changed, 27 insertions(+), 23 deletions(-) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastAnalyzer.kt index a3d2ad35956..eb08a07bd34 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastAnalyzer.kt @@ -35,7 +35,7 @@ abstract class FastAnalyzer>( 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?> { if (nInsns == 0) return frames diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FastStackAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FastStackAnalyzer.kt index df142317712..19a8724582f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FastStackAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FastStackAnalyzer.kt @@ -47,10 +47,11 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value internal open class FastStackAnalyzer>( owner: String, method: MethodNode, - interpreter: Interpreter + interpreter: Interpreter, + private val createFrame: (Int, Int) -> Frame = { nLocals, nStack -> Frame(nLocals, nStack) } ) : FastAnalyzer(owner, method, interpreter, pruneExceptionEdges = false) { @Suppress("UNCHECKED_CAST") - override fun newFrame(nLocals: Int, nStack: Int): F = Frame(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 diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt index 001021827aa..9abcfd3fe18 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackAnalyzer.kt @@ -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(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( + 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>() var maxExtraStackSize = 0; private set private val spilledStacks = hashMapOf>() + fun analyze() { + recordLoopEntryPointMarkers() + analyzer.analyze() + } + fun getStackToSpill(location: AbstractInsnNode): List? = spilledStacks[location] fun getActualStack(location: AbstractInsnNode): List? = - 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(nLocals, nStack) { private val extraStack = Stack() @@ -144,7 +147,7 @@ internal class FixStackAnalyzer( } } - fun pushAll(values: Collection) { + private fun pushAll(values: Collection) { 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) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackMethodTransformer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackMethodTransformer.kt index d7404717a47..cd773709533 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackMethodTransformer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/fixStack/FixStackMethodTransformer.kt @@ -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