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 new file mode 100644 index 00000000000..18d93dd0f01 --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FastAnalyzer.kt @@ -0,0 +1,73 @@ +/* + * Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.codegen.optimization.common + +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.* +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>( + protected val owner: String, + protected val method: MethodNode, + protected val interpreter: I, +) { + protected val nInsns = method.instructions.size() + protected val handlers: Array?> = arrayOfNulls(nInsns) + + protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) { + when { + insnType == AbstractInsnNode.JUMP_INSN -> + visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode) + insnType == AbstractInsnNode.LOOKUPSWITCH_INSN -> + visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current) + insnType == AbstractInsnNode.TABLESWITCH_INSN -> + visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current) + insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) -> + visitOpInsn(insnNode, current, insn) + else -> { + } + } + } + + protected abstract fun visitJumpInsnNode(insnNode: JumpInsnNode, current: F, insn: Int, insnOpcode: Int) + protected abstract fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: F) + protected abstract fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: F) + protected abstract fun visitOpInsn(insnNode: AbstractInsnNode, current: F, insn: Int) + + protected fun checkAssertions() { + if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) + throw AssertionError("Subroutines are deprecated since Java 6") + } + + protected fun AbstractInsnNode.indexOf() = + method.instructions.indexOf(this) + + protected fun F.dump(): String { + return buildString { + append("{\n") + append(" locals: [\n") + for (i in 0 until method.maxLocals) { + append(" #$i: ${this@dump.getLocal(i)}\n") + } + append(" ]\n") + val stackSize = this@dump.stackSize + append(" stack: size=") + append(stackSize) + if (stackSize == 0) { + append(" []\n") + } else { + append(" [\n") + for (i in 0 until stackSize) { + append(" #$i: ${this@dump.getStack(i)}\n") + } + append(" ]\n") + } + append("}\n") + } + } +} \ No newline at end of file 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 599cb913980..7fb2d0dbc55 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 @@ -46,21 +46,16 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value /** * @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer */ -@Suppress("DuplicatedCode") open class FastMethodAnalyzer @JvmOverloads constructor( - private val owner: String, - private val method: MethodNode, - private val interpreter: Interpreter, + owner: String, + method: MethodNode, + interpreter: Interpreter, private val pruneExceptionEdges: Boolean = false -) { - private val nInsns = method.instructions.size() - +) : FastAnalyzer, Frame>(owner, method, interpreter) { private val isMergeNode = findMergeNodes(method) - private val frames: Array?> = arrayOfNulls(nInsns) - private val handlers: Array?> = arrayOfNulls(nInsns) private val queued = BooleanArray(nInsns) private val queue = IntArray(nInsns) private var top = 0 @@ -102,18 +97,7 @@ open class FastMethodAnalyzer mergeControlFlowEdge(insn + 1, f, canReuse = true) } else { current.init(f).execute(insnNode, interpreter) - when { - insnType == AbstractInsnNode.JUMP_INSN -> - visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode) - insnType == AbstractInsnNode.LOOKUPSWITCH_INSN -> - visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current) - insnType == AbstractInsnNode.TABLESWITCH_INSN -> - visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current) - insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) -> - visitOpInsn(current, insn) - else -> { - } - } + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) } // Jump by an exception edge clears the stack, putting exception on top. @@ -155,7 +139,7 @@ open class FastMethodAnalyzer return frames } - internal fun initLocals(current: Frame) { + private fun initLocals(current: Frame) { current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))) val args = Type.getArgumentTypes(method.desc) var local = 0 @@ -178,22 +162,14 @@ open class FastMethodAnalyzer } } - private fun AbstractInsnNode.indexOf() = - method.instructions.indexOf(this) - fun getFrame(insn: AbstractInsnNode): Frame? = frames[insn.indexOf()] - private fun checkAssertions() { - if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) - throw AssertionError("Subroutines are deprecated since Java 6") - } - - private fun visitOpInsn(current: Frame, insn: Int) { + override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { mergeControlFlowEdge(insn + 1, current) } - private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame) { + override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame) { mergeControlFlowEdge(insnNode.dflt.indexOf(), current) // In most cases order of visiting switch labels should not matter // The only one is a tableswitch being added in the beginning of coroutine method, these switch' labels may lead @@ -205,14 +181,14 @@ open class FastMethodAnalyzer } } - private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame) { + override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame) { mergeControlFlowEdge(insnNode.dflt.indexOf(), current) for (label in insnNode.labels) { mergeControlFlowEdge(label.indexOf(), current) } } - private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame, insn: Int, insnOpcode: Int) { + override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame, insn: Int, insnOpcode: Int) { mergeControlFlowEdge(insnNode.label.indexOf(), current) if (insnOpcode != Opcodes.GOTO) { mergeControlFlowEdge(insn + 1, current) @@ -272,30 +248,6 @@ open class FastMethodAnalyzer } } - private fun Frame.dump(): String { - return buildString { - append("{\n") - append(" locals: [\n") - for (i in 0 until method.maxLocals) { - append(" #$i: ${this@dump.getLocal(i)}\n") - } - append(" ]\n") - val stackSize = this@dump.stackSize - append(" stack: size=") - append(stackSize) - if (stackSize == 0) { - append(" []\n") - } else { - append(" [\n") - for (i in 0 until stackSize) { - append(" #$i: ${this@dump.getStack(i)}\n") - } - append(" ]\n") - } - append("}\n") - } - } - companion object { fun findMergeNodes(method: MethodNode): BooleanArray { val isMergeNode = BooleanArray(method.instructions.size()) 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 7bbc080c1e8..61f58bada06 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,6 +34,7 @@ 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.* @@ -45,17 +46,13 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value /** * @see org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer */ -@Suppress("DuplicatedCode") internal open class FastStackAnalyzer( - private val owner: String, - val method: MethodNode, - protected val interpreter: Interpreter -) { - private val nInsns = method.instructions.size() - + owner: String, + method: MethodNode, + interpreter: Interpreter +) : FastAnalyzer, Frame>(owner, method, interpreter) { private val frames: Array?> = arrayOfNulls(nInsns) - private val handlers: Array?> = arrayOfNulls(nInsns) private val queued = BooleanArray(nInsns) private val queue = IntArray(nInsns) private var top = 0 @@ -98,19 +95,7 @@ internal open class FastStackAnalyzer( // Don't care about possibly incompatible return type current.execute(insnNode, interpreter) } - - when { - insnType == AbstractInsnNode.JUMP_INSN -> - visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode) - insnType == AbstractInsnNode.LOOKUPSWITCH_INSN -> - visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current) - insnType == AbstractInsnNode.TABLESWITCH_INSN -> - visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current) - insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) -> - visitOpInsn(insnNode, current, insn) - else -> { - } - } + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) } handlers[insn]?.forEach { tcb -> @@ -135,17 +120,10 @@ internal open class FastStackAnalyzer( return frames } - private fun AbstractInsnNode.indexOf() = method.instructions.indexOf(this) - fun getFrame(insn: AbstractInsnNode): Frame? = frames[insn.indexOf()] - private fun checkAssertions() { - if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) - throw AssertionError("Subroutines are deprecated since Java 6") - } - - private fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { + override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame, insn: Int) { processControlFlowEdge(current, insnNode, insn + 1) } @@ -153,7 +131,7 @@ internal open class FastStackAnalyzer( processControlFlowEdge(f, insnNode, insn + 1) } - private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame) { + override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame) { var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insnNode, jump) // In most cases order of visiting switch labels should not matter @@ -167,7 +145,7 @@ internal open class FastStackAnalyzer( } } - private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame) { + override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame) { var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insnNode, jump) for (label in insnNode.labels) { @@ -176,7 +154,7 @@ internal open class FastStackAnalyzer( } } - private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame, insn: Int, insnOpcode: Int) { + override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame, insn: Int, insnOpcode: Int) { if (insnOpcode != Opcodes.GOTO) { processControlFlowEdge(current, insnNode, insn + 1) } 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 77a14e1467b..427e58dcafe 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 @@ -103,8 +103,6 @@ internal class FixStackAnalyzer( override fun newFrame(nLocals: Int, nStack: Int): Frame = FixStackFrame(nLocals, nStack) - private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node) - inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame(nLocals, nStack) { val extraStack = Stack() @@ -207,7 +205,7 @@ internal class FixStackAnalyzer( private fun FixStackFrame.executeRestoreStackInTryCatch(insn: AbstractInsnNode) { val saveNode = context.saveStackMarkerForRestoreMarker[insn] val savedValues = spilledStacks.getOrElse(saveNode!!) { - throw AssertionError("${indexOf(insn)}: Restore stack is unavailable for ${indexOf(saveNode)}") + throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}") } pushAll(savedValues) } 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 e9d9c9f6133..1281fa74de3 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 @@ -34,28 +34,30 @@ 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.kotlin.codegen.optimization.common.isMeaningful import org.jetbrains.kotlin.utils.SmartList 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 -interface StoreLoadValue +interface StoreLoadValue : Value - -interface StoreLoadInterpreter { - fun uninitialized(): V - fun valueParameter(type: Type): V - fun store(insn: VarInsnNode): V - fun load(insn: VarInsnNode, value: V) - fun iinc(insn: IincInsnNode, value: V): V - fun merge(a: V, b: V): V +abstract class StoreLoadInterpreter : Interpreter(API_VERSION) { + abstract fun uninitialized(): V + abstract fun valueParameter(type: Type): V + abstract fun store(insn: VarInsnNode): V + abstract fun load(insn: VarInsnNode, value: V) + abstract fun iinc(insn: IincInsnNode, value: V): V } - @Suppress("UNCHECKED_CAST") -class StoreLoadFrame(val maxLocals: Int) { +class StoreLoadFrame(val maxLocals: Int) : Frame(maxLocals, 0) { private val locals = arrayOfNulls(maxLocals) operator fun get(index: Int): V = @@ -101,19 +103,14 @@ class StoreLoadFrame(val maxLocals: Int) { } } -@Suppress("DuplicatedCode") class FastStoreLoadAnalyzer( - private val owner: String, - private val method: MethodNode, - private val interpreter: StoreLoadInterpreter -) { - private val nInsns = method.instructions.size() - + owner: String, + method: MethodNode, + interpreter: StoreLoadInterpreter +) : FastAnalyzer, StoreLoadFrame>(owner, method, interpreter) { private val isMergeNode = BooleanArray(nInsns) - private val frames: Array?> = arrayOfNulls(nInsns) - private val handlers: Array?> = arrayOfNulls(nInsns) private val queued = BooleanArray(nInsns) private val queue = IntArray(nInsns) private var top = 0 @@ -144,18 +141,7 @@ class FastStoreLoadAnalyzer( mergeControlFlowEdge(insn + 1, f) } else { current.init(f).execute(insnNode, interpreter) - when { - insnType == AbstractInsnNode.JUMP_INSN -> - visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode) - insnType == AbstractInsnNode.LOOKUPSWITCH_INSN -> - visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current) - insnType == AbstractInsnNode.TABLESWITCH_INSN -> - visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current) - insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) -> - mergeControlFlowEdge(insn + 1, current) - else -> { - } - } + visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) } handlers[insn]?.forEach { tcb -> @@ -177,29 +163,25 @@ class FastStoreLoadAnalyzer( private fun newFrame(maxLocals: Int) = StoreLoadFrame(maxLocals) - private fun AbstractInsnNode.indexOf() = - method.instructions.indexOf(this) - - private fun checkAssertions() { - if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) - throw AssertionError("Subroutines are deprecated since Java 6") + override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame, insn: Int) { + mergeControlFlowEdge(insn + 1, current) } - private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame) { + override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame) { mergeControlFlowEdge(insnNode.dflt.indexOf(), current) for (label in insnNode.labels) { mergeControlFlowEdge(label.indexOf(), current) } } - private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame) { + override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame) { mergeControlFlowEdge(insnNode.dflt.indexOf(), current) for (label in insnNode.labels) { mergeControlFlowEdge(label.indexOf(), current) } } - private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame, insn: Int, insnOpcode: Int) { + override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame, insn: Int, insnOpcode: Int) { if (insnOpcode != Opcodes.GOTO) { mergeControlFlowEdge(insn + 1, current) } @@ -254,21 +236,25 @@ class FastStoreLoadAnalyzer( } } - internal fun initLocals(current: StoreLoadFrame) { + private fun initLocals(current: StoreLoadFrame) { val args = Type.getArgumentTypes(method.desc) var local = 0 if ((method.access and Opcodes.ACC_STATIC) == 0) { val ctype = Type.getObjectType(owner) - current[local++] = interpreter.valueParameter(ctype) + current.setLocal(local, interpreter.newValue(ctype)) + local++ } for (arg in args) { - current[local++] = interpreter.valueParameter(arg) + current.setLocal(local, interpreter.newValue(arg)) + local++ if (arg.size == 2) { - current[local++] = interpreter.uninitialized() + current.setLocal(local, interpreter.uninitialized()) + local++ } } while (local < method.maxLocals) { - current[local++] = interpreter.uninitialized() + current.setLocal(local, interpreter.uninitialized()) + local++ } } 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 5c71da7e2e4..3222da0a18c 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 @@ -134,6 +134,9 @@ class TemporaryValsAnalyzer { } private sealed class StoredValue : StoreLoadValue { + // `StoredValue` represent some abstract value that doesn't really have a definition of size. + // `getSize` can return either 1 or 2, so we use 1 here. + override fun getSize(): Int = 1 object Unknown : StoredValue() @@ -156,13 +159,13 @@ class TemporaryValsAnalyzer { private class StoreTrackingInterpreter( private val storeInsnToStoreData: Map - ) : StoreLoadInterpreter { + ) : StoreLoadInterpreter() { - override fun uninitialized(): StoredValue = - StoredValue.Unknown + override fun uninitialized(): StoredValue = StoredValue.Unknown - override fun valueParameter(type: Type): StoredValue = - StoredValue.Unknown + override fun newValue(type: Type?): StoredValue = StoredValue.Unknown + + override fun valueParameter(type: Type): StoredValue = StoredValue.Unknown override fun store(insn: VarInsnNode): StoredValue { val temporaryValData = storeInsnToStoreData[insn] @@ -218,5 +221,38 @@ class TemporaryValsAnalyzer { is StoredValue.DirtyStore -> this.temporaryVals else -> emptySet() } + + override fun copyOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue { + TODO("Not yet implemented") + } + + override fun newOperation(insn: AbstractInsnNode?): StoredValue { + TODO("Not yet implemented") + } + + override fun unaryOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue { + TODO("Not yet implemented") + } + + override fun binaryOperation(insn: AbstractInsnNode?, value1: StoredValue?, value2: StoredValue?): StoredValue { + TODO("Not yet implemented") + } + + override fun ternaryOperation( + insn: AbstractInsnNode?, + value1: StoredValue?, + value2: StoredValue?, + value3: StoredValue?, + ): StoredValue { + TODO("Not yet implemented") + } + + override fun naryOperation(insn: AbstractInsnNode?, values: MutableList?): StoredValue { + TODO("Not yet implemented") + } + + override fun returnOperation(insn: AbstractInsnNode?, value: StoredValue?, expected: StoredValue?) { + TODO("Not yet implemented") + } } }