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 50f7bf5b637..bce205d5eed 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 @@ -116,7 +116,7 @@ abstract class FastAnalyzer, F : Frame>( protected abstract fun newFrame(nLocals: Int, nStack: Int): F @Suppress("UNCHECKED_CAST") - fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F + protected fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F @Suppress("UNCHECKED_CAST") protected fun getFrame(index: Int): F? = frames[index] as? F 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 f65ba7b6664..3a9346867e0 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 @@ -46,12 +46,13 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value */ // This is a very specific version of method bytecode analyzer that doesn't perform any DFA, // but infers stack types for reachable instructions instead. -internal open class FastStackAnalyzer( +internal open class FastStackAnalyzer>( owner: String, method: MethodNode, interpreter: Interpreter -) : FastAnalyzer, Frame>(owner, method, interpreter) { - override fun newFrame(nLocals: Int, nStack: Int): Frame = Frame(nLocals, nStack) +) : FastAnalyzer, F>(owner, method, interpreter) { + @Suppress("UNCHECKED_CAST") + override fun newFrame(nLocals: Int, nStack: Int): F = Frame(nLocals, nStack) as F protected open fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean = true @@ -65,9 +66,9 @@ internal open class FastStackAnalyzer( insnIndex: Int, insnType: Int, insnOpcode: Int, - currentlyAnalyzing: Frame, - current: Frame, - handler: Frame, + currentlyAnalyzing: F, + current: F, + handler: F, ) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) @@ -92,15 +93,15 @@ internal open class FastStackAnalyzer( } } - override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { + override fun visitOpInsn(insnNode: AbstractInsnNode, current: F, insn: Int) { processControlFlowEdge(current, insnNode, insn + 1) } - private fun visitNopInsn(insnNode: AbstractInsnNode, f: Frame, insn: Int) { + private fun visitNopInsn(insnNode: AbstractInsnNode, f: F, insn: Int) { processControlFlowEdge(f, insnNode, insn + 1) } - override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame) { + override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: F) { var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insnNode, jump) // In most cases order of visiting switch labels should not matter @@ -114,7 +115,7 @@ internal open class FastStackAnalyzer( } } - override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame) { + override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: F) { var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insnNode, jump) for (label in insnNode.labels) { @@ -123,7 +124,7 @@ internal open class FastStackAnalyzer( } } - override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame, insn: Int, insnOpcode: Int) { + override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: F, insn: Int, insnOpcode: Int) { if (insnOpcode != Opcodes.GOTO) { processControlFlowEdge(current, insnNode, insn + 1) } @@ -131,13 +132,13 @@ internal open class FastStackAnalyzer( processControlFlowEdge(current, insnNode, jump) } - private fun processControlFlowEdge(current: Frame, insnNode: AbstractInsnNode, jump: Int) { + private fun processControlFlowEdge(current: F, insnNode: AbstractInsnNode, jump: Int) { if (visitControlFlowEdge(insnNode, jump)) { mergeControlFlowEdge(jump, current) } } - override fun mergeControlFlowEdge(dest: Int, frame: Frame, canReuse: Boolean) { + override fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean) { val destFrame = getFrame(dest) if (destFrame == null) { // Don't have to visit same instruction multiple times - we care only about "initial" stack state. 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 57f0a9ba286..001021827aa 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 @@ -32,10 +32,10 @@ import kotlin.math.max internal class FixStackAnalyzer( owner: String, - val method: MethodNode, + method: MethodNode, val context: FixStackContext, private val skipBreakContinueGotoEdges: Boolean = true -) { +) : FastStackAnalyzer(owner, method, FixStackInterpreter()) { companion object { // Stack size is always non-negative const val DEAD_CODE_STACK_SIZE = -1 @@ -43,10 +43,11 @@ internal class FixStackAnalyzer( private val loopEntryPointMarkers = hashMapOf>() - val maxExtraStackSize: Int get() = analyzer.maxExtraStackSize + var maxExtraStackSize = 0; private set + private val spilledStacks = hashMapOf>() fun getStackToSpill(location: AbstractInsnNode): List? = - analyzer.spilledStacks[location] + spilledStacks[location] fun getActualStack(location: AbstractInsnNode): List? = getFrame(location)?.getStackContent() @@ -70,11 +71,8 @@ internal class FixStackAnalyzer( return DEAD_CODE_STACK_SIZE } - private fun getFrame(location: AbstractInsnNode) = analyzer.getFrame(location) as? InternalAnalyzer.FixStackFrame - - fun analyze() { + override fun beforeAnalyze() { recordLoopEntryPointMarkers() - analyzer.analyze() } private fun recordLoopEntryPointMarkers() { @@ -87,133 +85,122 @@ internal class FixStackAnalyzer( } } - private val analyzer = InternalAnalyzer(owner) - - private inner class InternalAnalyzer(owner: String) : - FastStackAnalyzer(owner, method, FixStackInterpreter()) { - - val spilledStacks = hashMapOf>() - var maxExtraStackSize = 0; private set - - 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): Frame = - FixStackFrame(nLocals, nStack) - - inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { - val extraStack = Stack() - - override fun init(src: Frame): Frame { - extraStack.clear() - extraStack.addAll((src as FixStackFrame).extraStack) - return super.init(src) - } - - override fun clearStack() { - extraStack.clear() - super.clearStack() - } - - override fun execute(insn: AbstractInsnNode, interpreter: Interpreter) { - when { - PseudoInsn.SAVE_STACK_BEFORE_TRY.isa(insn) -> - executeSaveStackBeforeTry(insn) - PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) -> - executeRestoreStackInTryCatch(insn) - isBeforeInlineMarker(insn) -> - executeBeforeInlineCallMarker(insn) - isAfterInlineMarker(insn) -> - executeAfterInlineCallMarker(insn) - insn.opcode == Opcodes.RETURN -> - return - } - - super.execute(insn, interpreter) - } - - val stackSizeWithExtra: Int get() = super.getStackSize() + extraStack.size - - fun getStackContent(): List { - val savedStack = ArrayList() - for (i in 0 until super.getStackSize()) { - savedStack.add(super.getStack(i)) - } - savedStack.addAll(extraStack) - return savedStack - } - - override fun push(value: FixStackValue) { - if (super.getStackSize() < maxStackSize) { - super.push(value) - } else { - extraStack.add(value) - maxExtraStackSize = max(maxExtraStackSize, extraStack.size) - } - } - - fun pushAll(values: Collection) { - values.forEach { push(it) } - } - - override fun pop(): FixStackValue = - if (extraStack.isNotEmpty()) { - extraStack.pop() - } else { - super.pop() - } - - override fun setStack(i: Int, value: FixStackValue) { - if (i < super.getMaxStackSize()) { - super.setStack(i, value) - } else { - extraStack[i - maxStackSize] = value - } - } - - override fun merge(frame: Frame, interpreter: Interpreter): Boolean { - throw UnsupportedOperationException("Stack normalization should not merge frames") - } - - private fun executeBeforeInlineCallMarker(insn: AbstractInsnNode) { - saveStackAndClear(insn) - } - - private fun saveStackAndClear(insn: AbstractInsnNode) { - val savedValues = getStackContent() - spilledStacks[insn] = savedValues - clearStack() - } - - private fun executeAfterInlineCallMarker(insn: AbstractInsnNode) { - val beforeInlineMarker = context.openingInlineMethodMarker[insn] - if (stackSize > 0) { - val returnValue = pop() - clearStack() - val savedValues = spilledStacks[beforeInlineMarker] - pushAll(savedValues!!) - push(returnValue) - } else { - val savedValues = spilledStacks[beforeInlineMarker] - pushAll(savedValues!!) - } - } - - 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()}") - } - pushAll(savedValues) - } - - private fun executeSaveStackBeforeTry(insn: AbstractInsnNode) { - saveStackAndClear(insn) - } - } + 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() + + override fun init(src: Frame): Frame { + extraStack.clear() + extraStack.addAll((src as FixStackFrame).extraStack) + return super.init(src) + } + + override fun clearStack() { + extraStack.clear() + super.clearStack() + } + + override fun execute(insn: AbstractInsnNode, interpreter: Interpreter) { + when { + PseudoInsn.SAVE_STACK_BEFORE_TRY.isa(insn) -> + executeSaveStackBeforeTry(insn) + PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) -> + executeRestoreStackInTryCatch(insn) + isBeforeInlineMarker(insn) -> + executeBeforeInlineCallMarker(insn) + isAfterInlineMarker(insn) -> + executeAfterInlineCallMarker(insn) + insn.opcode == Opcodes.RETURN -> + return + } + + super.execute(insn, interpreter) + } + + val stackSizeWithExtra: Int get() = super.getStackSize() + extraStack.size + + fun getStackContent(): List { + val savedStack = ArrayList() + for (i in 0 until super.getStackSize()) { + savedStack.add(super.getStack(i)) + } + savedStack.addAll(extraStack) + return savedStack + } + + override fun push(value: FixStackValue) { + if (super.getStackSize() < maxStackSize) { + super.push(value) + } else { + extraStack.add(value) + maxExtraStackSize = max(maxExtraStackSize, extraStack.size) + } + } + + fun pushAll(values: Collection) { + values.forEach { push(it) } + } + + override fun pop(): FixStackValue = + if (extraStack.isNotEmpty()) { + extraStack.pop() + } else { + super.pop() + } + + override fun setStack(i: Int, value: FixStackValue) { + if (i < super.getMaxStackSize()) { + super.setStack(i, value) + } else { + extraStack[i - maxStackSize] = value + } + } + + override fun merge(frame: Frame, interpreter: Interpreter): Boolean { + throw UnsupportedOperationException("Stack normalization should not merge frames") + } + + private fun executeBeforeInlineCallMarker(insn: AbstractInsnNode) { + saveStackAndClear(insn) + } + + private fun saveStackAndClear(insn: AbstractInsnNode) { + val savedValues = getStackContent() + spilledStacks[insn] = savedValues + clearStack() + } + + private fun executeAfterInlineCallMarker(insn: AbstractInsnNode) { + val beforeInlineMarker = context.openingInlineMethodMarker[insn] + if (stackSize > 0) { + val returnValue = pop() + clearStack() + val savedValues = spilledStacks[beforeInlineMarker] + pushAll(savedValues!!) + push(returnValue) + } else { + val savedValues = spilledStacks[beforeInlineMarker] + pushAll(savedValues!!) + } + } + + 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()}") + } + pushAll(savedValues) + } + + private fun executeSaveStackBeforeTry(insn: AbstractInsnNode) { + saveStackAndClear(insn) + } + } }