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 4a172d3c77b..0c3d742b773 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 @@ -19,12 +19,14 @@ abstract class FastAnalyzer, F : Frame>( protected val owner: String, protected val method: MethodNode, protected val interpreter: I, + private val pruneExceptionEdges: Boolean, ) { - protected val nInsns = method.instructions.size() - protected val handlers: Array?> = arrayOfNulls(nInsns) - + private val nInsns = method.instructions.size() private val frames: Array?> = arrayOfNulls(nInsns) + protected val handlers: Array?> = arrayOfNulls(nInsns) + protected val isTcbStart = BooleanArray(nInsns) + private val queued = BooleanArray(nInsns) private val queue = IntArray(nInsns) private var top = 0 @@ -56,11 +58,9 @@ abstract class FastAnalyzer, F : Frame>( queued[insn] = false val insnNode = method.instructions[insn] - val insnOpcode = insnNode.opcode - val insnType = insnNode.type try { - analyzeInstruction(insnNode, insn, insnType, insnOpcode, f, current, handler) + analyzeInstruction(insnNode, insn, f, current, handler) } catch (e: AnalyzerException) { throw AnalyzerException( e.node, @@ -111,15 +111,53 @@ abstract class FastAnalyzer, F : Frame>( protected abstract fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean = false) - protected abstract fun analyzeInstruction( + private fun analyzeInstruction( insnNode: AbstractInsnNode, insnIndex: Int, - insnType: Int, - insnOpcode: Int, currentlyAnalyzing: F, current: F, handler: F - ) + ) { + val insnOpcode = insnNode.opcode + val insnType = insnNode.type + + if (insnType == AbstractInsnNode.LABEL || + insnType == AbstractInsnNode.LINE || + insnType == AbstractInsnNode.FRAME || + insnOpcode == Opcodes.NOP + ) { + visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) + } else { + current.init(currentlyAnalyzing) + if (insnOpcode != Opcodes.RETURN) { + // Don't care about possibly incompatible return type + current.execute(insnNode, interpreter) + } + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) + } + + // Jump by an exception edge clears the stack, putting exception on top. + // So, unless we have a store operation, anything we change on stack would be lost, + // and there's no need to analyze exception handler again. + // Add an exception edge from TCB start to make sure handler itself is still visited. + if (!pruneExceptionEdges || + insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE || + insnOpcode == Opcodes.IINC || + isTcbStart[insnIndex] + ) { + handlers[insnIndex]?.forEach { tcb -> + val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") + val jump = tcb.handler.indexOf() + + handler.init(currentlyAnalyzing) + if (handler.maxStackSize > 0) { + handler.clearStack() + handler.push(interpreter.newExceptionValue(tcb, handler, exnType)) + } + mergeControlFlowEdge(jump, handler) + } + } + } protected abstract fun newFrame(nLocals: Int, nStack: Int): F @@ -133,7 +171,7 @@ abstract class FastAnalyzer, F : Frame>( frames[index] = newFrame } - protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) { + private fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) { when { insnType == AbstractInsnNode.JUMP_INSN -> visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode) @@ -148,7 +186,7 @@ abstract class FastAnalyzer, F : Frame>( } } - protected fun visitNopInsn(insnNode: AbstractInsnNode, current: F, insn: Int) { + private fun visitNopInsn(insnNode: AbstractInsnNode, current: F, insn: Int) { processControlFlowEdge(current, insnNode, insn + 1, canReuse = true) } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastMethodAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastMethodAnalyzer.kt index 05b82218ee5..accbc9501cb 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastMethodAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastMethodAnalyzer.kt @@ -33,8 +33,6 @@ package org.jetbrains.kotlin.codegen.optimization.common -import org.jetbrains.org.objectweb.asm.Opcodes -import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException import org.jetbrains.org.objectweb.asm.tree.analysis.Frame @@ -49,11 +47,10 @@ class FastMethodAnalyzer owner: String, method: MethodNode, interpreter: Interpreter, - private val pruneExceptionEdges: Boolean = false, + pruneExceptionEdges: Boolean = false, private val createFrame: (Int, Int) -> Frame = { nLocals, nStack -> Frame(nLocals, nStack) } -) : FastAnalyzer, Frame>(owner, method, interpreter) { +) : FastAnalyzer, Frame>(owner, method, interpreter, pruneExceptionEdges) { private val isMergeNode = findMergeNodes(method) - private val isTcbStart = BooleanArray(nInsns) override fun newFrame(nLocals: Int, nStack: Int): Frame = createFrame(nLocals, nStack) @@ -63,47 +60,6 @@ class FastMethodAnalyzer } } - override fun analyzeInstruction( - insnNode: AbstractInsnNode, - insnIndex: Int, - insnType: Int, - insnOpcode: Int, - currentlyAnalyzing: Frame, - current: Frame, - handler: Frame, - ) { - if (insnType == AbstractInsnNode.LABEL || - insnType == AbstractInsnNode.LINE || - insnType == AbstractInsnNode.FRAME || - insnOpcode == Opcodes.NOP - ) { - visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) - } else { - current.init(currentlyAnalyzing).execute(insnNode, interpreter) - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) - } - - // Jump by an exception edge clears the stack, putting exception on top. - // So, unless we have a store operation, anything we change on stack would be lost, - // and there's no need to analyze exception handler again. - // Add an exception edge from TCB start to make sure handler itself is still visited. - if (!pruneExceptionEdges || - insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE || - insnOpcode == Opcodes.IINC || - isTcbStart[insnIndex] - ) { - handlers[insnIndex]?.forEach { tcb -> - val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") - val jump = tcb.handler.indexOf() - - handler.init(currentlyAnalyzing) - handler.clearStack() - handler.push(interpreter.newExceptionValue(tcb, handler, exnType)) - mergeControlFlowEdge(jump, handler) - } - } - } - /** * Updates frame at the index [dest] with its old value if provided and previous control flow node frame [frame]. * Reuses old frame when possible and when [canReuse] is true. 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 a12e78a6238..0abe45cb297 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 @@ -34,9 +34,6 @@ package org.jetbrains.kotlin.codegen.optimization.fixStack import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer -import org.jetbrains.org.objectweb.asm.Opcodes -import org.jetbrains.org.objectweb.asm.Type -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter @@ -51,48 +48,13 @@ internal open class FastStackAnalyzer>( owner: String, method: MethodNode, interpreter: Interpreter -) : FastAnalyzer, F>(owner, method, interpreter) { +) : FastAnalyzer, F>(owner, method, interpreter, pruneExceptionEdges = false) { @Suppress("UNCHECKED_CAST") override fun newFrame(nLocals: Int, nStack: Int): F = Frame(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 - override fun analyzeInstruction( - insnNode: AbstractInsnNode, - insnIndex: Int, - insnType: Int, - insnOpcode: Int, - currentlyAnalyzing: F, - current: F, - handler: F, - ) { - if (insnType == AbstractInsnNode.LABEL || - insnType == AbstractInsnNode.LINE || - insnType == AbstractInsnNode.FRAME || - insnOpcode == Opcodes.NOP - ) { - visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) - } else { - current.init(currentlyAnalyzing) - if (insnOpcode != Opcodes.RETURN) { - // Don't care about possibly incompatible return type - current.execute(insnNode, interpreter) - } - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) - } - - handlers[insnIndex]?.forEach { tcb -> - val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") - val jump = tcb.handler.indexOf() - - handler.init(currentlyAnalyzing) - handler.clearStack() - handler.push(interpreter.newExceptionValue(tcb, handler, exnType)) - mergeControlFlowEdge(jump, handler) - } - } - override fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean) { val oldFrame = getFrame(dest) val changes = when { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/FastStoreLoadAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/FastStoreLoadAnalyzer.kt index 4708c8788bc..7362057bdf5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/FastStoreLoadAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/FastStoreLoadAnalyzer.kt @@ -67,36 +67,9 @@ class FastStoreLoadAnalyzer( owner: String, method: MethodNode, interpreter: Interpreter -) : FastAnalyzer, StoreLoadFrame>(owner, method, interpreter) { +) : FastAnalyzer, StoreLoadFrame>(owner, method, interpreter, pruneExceptionEdges = false) { private val isMergeNode = FastMethodAnalyzer.findMergeNodes(method) - override fun analyzeInstruction( - insnNode: AbstractInsnNode, - insnIndex: Int, - insnType: Int, - insnOpcode: Int, - currentlyAnalyzing: StoreLoadFrame, - current: StoreLoadFrame, - handler: StoreLoadFrame, - ) { - if (insnType == AbstractInsnNode.LABEL || - insnType == AbstractInsnNode.LINE || - insnType == AbstractInsnNode.FRAME || - insnOpcode == Opcodes.NOP - ) { - visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) - } else { - current.init(currentlyAnalyzing).execute(insnNode, interpreter) - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) - } - - handlers[insnIndex]?.forEach { tcb -> - val jump = tcb.handler.indexOf() - handler.init(currentlyAnalyzing) - mergeControlFlowEdge(jump, handler) - } - } - override fun newFrame(nLocals: Int, nStack: Int): StoreLoadFrame = StoreLoadFrame(nLocals) override fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame, canReuse: Boolean) {