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 6799f4957dc..ebdac9a9591 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 @@ -5,14 +5,16 @@ package org.jetbrains.kotlin.codegen.optimization.common +import org.jetbrains.kotlin.codegen.inline.insnText import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.org.objectweb.asm.Opcodes 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 import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter import org.jetbrains.org.objectweb.asm.tree.analysis.Value -abstract class FastAnalyzer, F: Frame>( +abstract class FastAnalyzer, F : Frame>( protected val owner: String, protected val method: MethodNode, protected val interpreter: I, @@ -20,6 +22,75 @@ abstract class FastAnalyzer, F: Frame>( protected val nInsns = method.instructions.size() protected val handlers: Array?> = arrayOfNulls(nInsns) + private val frames: Array?> = arrayOfNulls(nInsns) + + private val queued = BooleanArray(nInsns) + private val queue = IntArray(nInsns) + private var top = 0 + + protected fun analyzeInner() { + val current = newFrame(method.maxLocals, method.maxStack) + val handler = newFrame(method.maxLocals, method.maxStack) + initLocals(current) + mergeControlFlowEdge(0, current) + + while (top > 0) { + val insn = queue[--top] + + @Suppress("UNCHECKED_CAST") + val f = frames[insn]!! as F + 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) + } catch (e: AnalyzerException) { + throw AnalyzerException( + e.node, + "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}", + e + ) + } catch (e: Exception) { + throw AnalyzerException( + insnNode, + "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}", + e + ) + } + } + } + + protected abstract fun initLocals(current: F) + + protected abstract fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean = false) + + protected abstract fun analyzeInstruction( + insnNode: AbstractInsnNode, + insnIndex: Int, + insnType: Int, + insnOpcode: Int, + currentlyAnalyzing: F, + current: F, + handler: F + ) + + protected abstract fun newFrame(nLocals: Int, nStack: Int): F + + @Suppress("UNCHECKED_CAST") + fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F + + @Suppress("UNCHECKED_CAST") + protected fun getFrame(index: Int): F? = frames[index] as? F + + protected fun setFrame(index: Int, newFrame: F) { + frames[index] = newFrame + } + + protected fun getFrames(): Array?> = frames + protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) { when { insnType == AbstractInsnNode.JUMP_INSN -> @@ -82,6 +153,13 @@ abstract class FastAnalyzer, F: Frame>( insnHandlers.add(tcb) } + protected fun updateQueue(changes: Boolean, dest: Int) { + if (changes && !queued[dest]) { + queued[dest] = true + queue[top++] = dest + } + } + protected fun F.dump(): String { return buildString { append("{\n") 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 2065974e7b2..972437199c9 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,7 +33,6 @@ package org.jetbrains.kotlin.codegen.optimization.common -import org.jetbrains.kotlin.codegen.inline.insnText import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.tree.* @@ -53,68 +52,33 @@ open class FastMethodAnalyzer private val pruneExceptionEdges: Boolean = false ) : FastAnalyzer, Frame>(owner, method, interpreter) { private val isMergeNode = findMergeNodes(method) - private val frames: Array?> = arrayOfNulls(nInsns) + private val isTcbStart = BooleanArray(nInsns) - private val queued = BooleanArray(nInsns) - private val queue = IntArray(nInsns) - private var top = 0 - - protected open fun newFrame(nLocals: Int, nStack: Int): Frame = + override fun newFrame(nLocals: Int, nStack: Int): Frame = Frame(nLocals, nStack) fun analyze(): Array?> { - if (nInsns == 0) return frames + if (nInsns == 0) return getFrames() checkAssertions() computeExceptionHandlers(method) - val isTcbStart = BooleanArray(nInsns) for (tcb in method.tryCatchBlocks) { isTcbStart[tcb.start.indexOf() + 1] = true } - val current = newFrame(method.maxLocals, method.maxStack) - val handler = newFrame(method.maxLocals, method.maxStack) - initLocals(current) - mergeControlFlowEdge(0, current) + analyzeInner() - while (top > 0) { - val insn = queue[--top] - val f = frames[insn]!! - queued[insn] = false - - val insnNode = method.instructions[insn] - val insnOpcode = insnNode.opcode - val insnType = insnNode.type - - try { - analyzeInstruction(insnType, insnOpcode, insn, f, current, insnNode, isTcbStart, handler) - } catch (e: AnalyzerException) { - throw AnalyzerException( - e.node, - "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}", - e - ) - } catch (e: Exception) { - throw AnalyzerException( - insnNode, - "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}", - e - ) - } - } - - return frames + return getFrames() } - private fun analyzeInstruction( + override fun analyzeInstruction( + insnNode: AbstractInsnNode, + insnIndex: Int, insnType: Int, insnOpcode: Int, - insn: Int, - f: Frame, + currentlyAnalyzing: Frame, current: Frame, - insnNode: AbstractInsnNode, - isTcbStart: BooleanArray, handler: Frame, ) { if (insnType == AbstractInsnNode.LABEL || @@ -122,10 +86,10 @@ open class FastMethodAnalyzer insnType == AbstractInsnNode.FRAME || insnOpcode == Opcodes.NOP ) { - mergeControlFlowEdge(insn + 1, f, canReuse = true) + mergeControlFlowEdge(insnIndex + 1, currentlyAnalyzing, canReuse = true) } else { - current.init(f).execute(insnNode, interpreter) - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) + current.init(currentlyAnalyzing).execute(insnNode, interpreter) + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) } // Jump by an exception edge clears the stack, putting exception on top. @@ -135,13 +99,13 @@ open class FastMethodAnalyzer if (!pruneExceptionEdges || insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE || insnOpcode == Opcodes.IINC || - isTcbStart[insn] + isTcbStart[insnIndex] ) { - handlers[insn]?.forEach { tcb -> + handlers[insnIndex]?.forEach { tcb -> val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") val jump = tcb.handler.indexOf() - handler.init(f) + handler.init(currentlyAnalyzing) handler.clearStack() handler.push(interpreter.newExceptionValue(tcb, handler, exnType)) mergeControlFlowEdge(jump, handler) @@ -149,7 +113,7 @@ open class FastMethodAnalyzer } } - private fun initLocals(current: Frame) { + override fun initLocals(current: Frame) { current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))) val args = Type.getArgumentTypes(method.desc) var local = 0 @@ -172,9 +136,6 @@ open class FastMethodAnalyzer } } - fun getFrame(insn: AbstractInsnNode): Frame? = - frames[insn.indexOf()] - override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { mergeControlFlowEdge(insn + 1, current) } @@ -210,15 +171,15 @@ open class FastMethodAnalyzer * Reuses old frame when possible and when [canReuse] is true. * If updated, adds the frame to the queue */ - private fun mergeControlFlowEdge(dest: Int, frame: Frame, canReuse: Boolean = false) { - val oldFrame = frames[dest] + override fun mergeControlFlowEdge(dest: Int, frame: Frame, canReuse: Boolean) { + val oldFrame = getFrame(dest) val changes = when { canReuse && !isMergeNode[dest] -> { - frames[dest] = frame + setFrame(dest, frame) true } oldFrame == null -> { - frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) } + setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) }) true } !isMergeNode[dest] -> { @@ -232,10 +193,7 @@ open class FastMethodAnalyzer throw AnalyzerException(null, "${e.message}\nframe: ${frame.dump()}\noldFrame: ${oldFrame.dump()}") } } - if (changes && !queued[dest]) { - queued[dest] = true - queue[top++] = dest - } + updateQueue(changes, dest) } companion object { 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 f3bac484a35..e15758e5999 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 @@ -33,12 +33,10 @@ package org.jetbrains.kotlin.codegen.optimization.fixStack -import org.jetbrains.kotlin.codegen.inline.insnText 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.* -import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter import org.jetbrains.org.objectweb.asm.tree.analysis.Value @@ -51,20 +49,14 @@ internal open class FastStackAnalyzer( method: MethodNode, interpreter: Interpreter ) : FastAnalyzer, Frame>(owner, method, interpreter) { - private val frames: Array?> = arrayOfNulls(nInsns) - - private val queued = BooleanArray(nInsns) - private val queue = IntArray(nInsns) - private var top = 0 - - protected open fun newFrame(nLocals: Int, nStack: Int): Frame = Frame(nLocals, nStack) + override fun newFrame(nLocals: Int, nStack: Int): Frame = Frame(nLocals, nStack) protected open fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean = true protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true fun analyze(): Array?> { - if (nInsns == 0) return frames + if (nInsns == 0) return getFrames() // This is a very specific version of method bytecode analyzer that doesn't perform any DFA, // but infers stack types for reachable instructions instead. @@ -74,56 +66,36 @@ internal open class FastStackAnalyzer( // Don't have to visit same exception handler multiple times - we care only about stack state at TCB start. computeExceptionHandlers(method, forEachInsn = false) - val current = newFrame(method.maxLocals, method.maxStack) - val handler = newFrame(method.maxLocals, method.maxStack) - initControlFlowAnalysis(current, method, owner) + analyzeInner() - while (top > 0) { - val insn = queue[--top] - val f = frames[insn]!! - queued[insn] = false - - val insnNode = method.instructions[insn] - val insnOpcode = insnNode.opcode - val insnType = insnNode.type - - try { - analyzeInstruction(insnType, insnNode, f, insn, current, insnOpcode, handler) - } catch (e: AnalyzerException) { - throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e) - } catch (e: Exception) { - throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e) - } - } - - return frames + return getFrames() } - private fun analyzeInstruction( - insnType: Int, + override fun analyzeInstruction( insnNode: AbstractInsnNode, - f: Frame, - insn: Int, - current: Frame, + insnIndex: Int, + insnType: Int, insnOpcode: Int, + currentlyAnalyzing: Frame, + current: Frame, handler: Frame, ) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { - visitNopInsn(insnNode, f, insn) + visitNopInsn(insnNode, currentlyAnalyzing, insnIndex) } else { - current.init(f) + current.init(currentlyAnalyzing) if (insnOpcode != Opcodes.RETURN) { // Don't care about possibly incompatible return type current.execute(insnNode, interpreter) } - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) } - handlers[insn]?.forEach { tcb -> + handlers[insnIndex]?.forEach { tcb -> val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") val jump = tcb.handler.indexOf() - if (visitControlFlowExceptionEdge(insn, tcb.handler.indexOf())) { - handler.init(f) + if (visitControlFlowExceptionEdge(insnIndex, jump)) { + handler.init(currentlyAnalyzing) handler.clearStack() handler.push(interpreter.newValue(exnType)) mergeControlFlowEdge(jump, handler) @@ -131,9 +103,6 @@ internal open class FastStackAnalyzer( } } - fun getFrame(insn: AbstractInsnNode): Frame? = - frames[insn.indexOf()] - override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { processControlFlowEdge(current, insnNode, insn + 1) } @@ -179,11 +148,11 @@ internal open class FastStackAnalyzer( } } - private fun initControlFlowAnalysis(current: Frame, m: MethodNode, owner: String) { - current.setReturn(interpreter.newValue(Type.getReturnType(m.desc))) - val args = Type.getArgumentTypes(m.desc) + override fun initLocals(current: Frame) { + current.setReturn(interpreter.newValue(Type.getReturnType(method.desc))) + val args = Type.getArgumentTypes(method.desc) var local = 0 - if ((m.access and Opcodes.ACC_STATIC) == 0) { + if ((method.access and Opcodes.ACC_STATIC) == 0) { val ctype = Type.getObjectType(owner) current.setLocal(local++, interpreter.newValue(ctype)) } @@ -193,21 +162,17 @@ internal open class FastStackAnalyzer( current.setLocal(local++, interpreter.newValue(null)) } } - while (local < m.maxLocals) { + while (local < method.maxLocals) { current.setLocal(local++, interpreter.newValue(null)) } - mergeControlFlowEdge(0, current) } - private fun mergeControlFlowEdge(dest: Int, frame: Frame) { - val destFrame = frames[dest] + override fun mergeControlFlowEdge(dest: Int, frame: Frame, 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. - frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) } - if (!queued[dest]) { - queued[dest] = true - queue[top++] = dest - } + setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) }) + updateQueue(true, dest) } } 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 c9d8cc71119..665bcc20a91 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 @@ -33,13 +33,11 @@ package org.jetbrains.kotlin.codegen.optimization.temporaryVals -import org.jetbrains.kotlin.codegen.inline.insnText import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION 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 import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter import org.jetbrains.org.objectweb.asm.tree.analysis.Value @@ -107,70 +105,43 @@ class FastStoreLoadAnalyzer( interpreter: StoreLoadInterpreter ) : FastAnalyzer, StoreLoadFrame>(owner, method, interpreter) { private val isMergeNode = BooleanArray(nInsns) - private val frames: Array?> = arrayOfNulls(nInsns) - private val queued = BooleanArray(nInsns) - private val queue = IntArray(nInsns) - private var top = 0 - - fun analyze(): Array?> { - if (nInsns == 0) return frames + fun analyze(): Array?> { + if (nInsns == 0) return getFrames() checkAssertions() computeExceptionHandlers(method) initMergeNodes() - val current = newFrame(method.maxLocals) - val handler = newFrame(method.maxLocals) - initLocals(current) - mergeControlFlowEdge(0, current) + analyzeInner() - while (top > 0) { - val insn = queue[--top] - val f = frames[insn]!! - queued[insn] = false - - val insnNode = method.instructions[insn] - val insnOpcode = insnNode.opcode - val insnType = insnNode.type - - try { - analyzeInstruction(insnType, insn, f, current, insnNode, insnOpcode, handler) - } catch (e: AnalyzerException) { - throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}", e) - } catch (e: Exception) { - throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}", e) - } - } - - return frames + return getFrames() } - private fun analyzeInstruction( - insnType: Int, - insn: Int, - f: StoreLoadFrame, - current: StoreLoadFrame, + 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) { - mergeControlFlowEdge(insn + 1, f) + mergeControlFlowEdge(insnIndex + 1, currentlyAnalyzing) } else { - current.init(f).execute(insnNode, interpreter) - visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) + current.init(currentlyAnalyzing).execute(insnNode, interpreter) + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex) } - handlers[insn]?.forEach { tcb -> + handlers[insnIndex]?.forEach { tcb -> val jump = tcb.handler.indexOf() - handler.init(f) + handler.init(currentlyAnalyzing) mergeControlFlowEdge(jump, handler) } } - private fun newFrame(maxLocals: Int) = - StoreLoadFrame(maxLocals) + override fun newFrame(nLocals: Int, nStack: Int): StoreLoadFrame = StoreLoadFrame(nLocals) override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame, insn: Int) { mergeControlFlowEdge(insn + 1, current) @@ -225,7 +196,7 @@ class FastStoreLoadAnalyzer( } } - private fun initLocals(current: StoreLoadFrame) { + override fun initLocals(current: StoreLoadFrame) { val args = Type.getArgumentTypes(method.desc) var local = 0 if ((method.access and Opcodes.ACC_STATIC) == 0) { @@ -247,11 +218,11 @@ class FastStoreLoadAnalyzer( } } - private fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame) { - val oldFrame = frames[dest] + override fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame, canReuse: Boolean) { + val oldFrame = getFrame(dest) val changes = when { oldFrame == null -> { - frames[dest] = newFrame(frame.maxLocals).init(frame) + setFrame(dest, newFrame(frame.maxLocals, 0).init(frame)) true } !isMergeNode[dest] -> { @@ -261,10 +232,6 @@ class FastStoreLoadAnalyzer( else -> oldFrame.merge(frame, interpreter) } - if (changes && !queued[dest]) { - queued[dest] = true - queue[top++] = dest - } + updateQueue(changes, dest) } - } \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/TemporaryVals.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/TemporaryVals.kt index 3222da0a18c..e52ea9b6c7f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/TemporaryVals.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/temporaryVals/TemporaryVals.kt @@ -106,7 +106,7 @@ class TemporaryValsAnalyzer { // Exclude stores observed at LVT liveness range start using information from bytecode analysis. for (lv in methodNode.localVariables) { val frameAtStart = frames[insnList.indexOf(lv.start)] ?: continue - when (val valueAtStart = frameAtStart[lv.index]) { + when (val valueAtStart = frameAtStart.getLocal(lv.index)) { is StoredValue.Store -> valueAtStart.temporaryVal.isDirty = true is StoredValue.DirtyStore ->