JVM FastFrameSizeCalculator
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FastFrameSizeCalculator.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/FastFrameSizeCalculator.kt
new file mode 100644
index 0000000..d4d63dd
--- /dev/null
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FastFrameSizeCalculator.kt
@@ -0,0 +1,229 @@
+/*
+ * Copyright 2010-2022 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
+
+import org.jetbrains.org.objectweb.asm.Opcodes
+import org.jetbrains.org.objectweb.asm.Type
+import org.jetbrains.org.objectweb.asm.tree.*
+import java.util.*
+
+internal val STACK_SIZE_CHANGE_BY_OPCODE = run {
+ val s = "EFFFFFFFFGGFFFGGFFFEEFGFGFEEEEEEEEEEEEEEEEEEEEDEDEDDDDD" +
+ "CDCDEEEEEEEEEEEEEEEEEEEEBABABBBBDCFFFGGGEDCDCDCDCDCDCDCDCD" +
+ "CDCEEEEDDDDDDDCDCDCEFEFDDEEFFDEDEEEBDDBBDDDDDDCCCCCCCCEFED" +
+ "DDCDCDEEEEEEEEEEFEEEEEEDDEEDDEE"
+ IntArray(s.length) {
+ s[it].code - 'E'.code
+ }
+}
+
+class FastFrameSizeCalculator(private val methodNode: MethodNode) {
+ private val insnList = methodNode.instructions
+ private val insnArray = insnList.toArray()
+ private val nInsns = insnArray.size
+ private val stackSize = IntArray(nInsns)
+
+ private val queue = IntArray(nInsns)
+ private var top = 0
+
+ private var maxStack = 0
+
+ fun updateMaxLocals() {
+ var paramsSize = Type.getArgumentsAndReturnSizes(methodNode.desc) shr 2
+ if (methodNode.access and Opcodes.ACC_STATIC != 0) {
+ --paramsSize
+ }
+
+ var maxLocals = paramsSize
+
+ for (insnNode in insnArray) {
+ when (insnNode.opcode) {
+ Opcodes.ASTORE, Opcodes.ISTORE, Opcodes.FSTORE -> {
+ val varInsnNode = insnNode as VarInsnNode
+ val varSlot = varInsnNode.`var`
+ if (maxLocals < varSlot) {
+ maxLocals = varSlot
+ }
+ }
+
+ Opcodes.DSTORE, Opcodes.LSTORE -> {
+ val varInsnNode = insnNode as VarInsnNode
+ val varSlot = varInsnNode.`var` + 1
+ if (maxLocals < varSlot) {
+ maxLocals = varSlot
+ }
+ }
+ }
+ }
+
+ for (lv in methodNode.localVariables) {
+ val lvSize = when (lv.desc[0]) {
+ 'L', 'D' -> 1
+ else -> 0
+ }
+ val lvSlot = lv.index + lvSize
+ if (maxLocals < lvSlot) {
+ maxLocals = lvSlot
+ }
+ }
+
+ methodNode.maxLocals = maxLocals
+ }
+
+ fun updateMaxStack() {
+ if (nInsns == 0) {
+ methodNode.maxStack = 0
+ return
+ }
+
+ Arrays.fill(stackSize, -1)
+ for (tcb in methodNode.tryCatchBlocks) {
+ val tcbHandlerIndex = tcb.handler.indexOf()
+ stackSize[tcbHandlerIndex] = 1
+ queue[top++] = tcbHandlerIndex
+ }
+ stackSize[0] = 0
+ queue[top++] = 0
+
+ while (top > 0) {
+ val insn = queue[--top]
+ val insnStackSize = stackSize[insn]
+ val insnNode = insnArray[insn]
+
+ // TODO -> insnNode.accept(MethodVisitor) ?
+ when (insnNode.type) {
+ AbstractInsnNode.LABEL,
+ AbstractInsnNode.LINE,
+ AbstractInsnNode.FRAME,
+ AbstractInsnNode.IINC_INSN ->
+ visitEdge(insn, insn + 1, insnStackSize)
+ AbstractInsnNode.INSN ->
+ visitInsn(insnNode.opcode, insnStackSize, insn)
+ AbstractInsnNode.INT_INSN,
+ AbstractInsnNode.VAR_INSN,
+ AbstractInsnNode.TYPE_INSN ->
+ visitSimpleInsn(insnNode.opcode, insnStackSize, insn)
+ AbstractInsnNode.FIELD_INSN ->
+ visitFieldInsn(insnNode as FieldInsnNode, insnStackSize, insn)
+ AbstractInsnNode.METHOD_INSN ->
+ visitMethodInsn(insnNode as MethodInsnNode, insnStackSize, insn)
+ AbstractInsnNode.INVOKE_DYNAMIC_INSN ->
+ visitIndyInsn(insnNode as InvokeDynamicInsnNode, insnStackSize, insn)
+ AbstractInsnNode.JUMP_INSN ->
+ visitJumpInsn(insnNode as JumpInsnNode, insnStackSize, insn)
+ AbstractInsnNode.LDC_INSN ->
+ visitLdcInsn(insnNode as LdcInsnNode, insnStackSize, insn)
+ AbstractInsnNode.TABLESWITCH_INSN -> {
+ val switchNode = insnNode as TableSwitchInsnNode
+ visitSwitchInsn(switchNode.dflt, switchNode.labels, insnStackSize, insn)
+ }
+ AbstractInsnNode.LOOKUPSWITCH_INSN -> {
+ val switchNode = insnNode as LookupSwitchInsnNode
+ visitSwitchInsn(switchNode.dflt, switchNode.labels, insnStackSize, insn)
+ }
+ AbstractInsnNode.MULTIANEWARRAY_INSN ->
+ visitMultiNewArrayInsn(insnNode as MultiANewArrayInsnNode, insnStackSize, insn)
+ }
+ }
+
+ methodNode.maxStack = maxStack
+ }
+
+ private fun visitMultiNewArrayInsn(insnNode: MultiANewArrayInsnNode, insnStackSize: Int, insn: Int) {
+ visitEdge(insn, insn + 1, insnStackSize + insnNode.dims - 1)
+ }
+
+ private fun visitSwitchInsn(dflt: LabelNode, labels: List<LabelNode>, insnStackSize: Int, insn: Int) {
+ val outputStackSize = insnStackSize - 1
+ for (label in labels) {
+ visitEdge(insn, label.indexOf(), outputStackSize)
+ }
+ visitEdge(insn, dflt.indexOf(), outputStackSize)
+ }
+
+ private fun visitLdcInsn(insnNode: LdcInsnNode, insnStackSize: Int, insn: Int) {
+ val sizeDelta = when (insnNode.cst) {
+ is Long, is Double -> 2
+ else -> 1
+ }
+ visitEdge(insn, insn + 1, insnStackSize + sizeDelta)
+ }
+
+ private fun visitIndyInsn(insnNode: InvokeDynamicInsnNode, insnStackSize: Int, insn: Int) {
+ val argSize = Type.getArgumentsAndReturnSizes(insnNode.desc)
+ val sizeDelta = (argSize and 0x03) - (argSize shr 2) + 1
+ visitEdge(insn, insn + 1, insnStackSize + sizeDelta)
+ }
+
+ private fun visitMethodInsn(insnNode: MethodInsnNode, insnStackSize: Int, insn: Int) {
+ val argSize = Type.getArgumentsAndReturnSizes(insnNode.desc)
+ val sizeDelta = if (insnNode.opcode == Opcodes.INVOKESTATIC) {
+ (argSize and 0x03) - (argSize shr 2) + 1
+ } else {
+ (argSize and 0x03) - (argSize shr 2)
+ }
+ visitEdge(insn, insn + 1, insnStackSize + sizeDelta)
+ }
+
+ private fun visitFieldInsn(insnNode: FieldInsnNode, insnStackSize: Int, insn: Int) {
+ val fieldSize = when (insnNode.desc[0]) {
+ 'D', 'J' -> 2
+ else -> 1
+ }
+ val sizeDelta = when (insnNode.opcode) {
+ Opcodes.GETSTATIC -> fieldSize
+ Opcodes.PUTSTATIC -> -fieldSize
+ Opcodes.GETFIELD -> fieldSize - 1
+ else -> -fieldSize - 1
+ }
+ visitEdge(insn, insn + 1, insnStackSize + sizeDelta)
+ }
+
+ private fun visitSimpleInsn(insnOpcode: Int, insnStackSize: Int, insn: Int) {
+ val outputStackSize = insnStackSize + STACK_SIZE_CHANGE_BY_OPCODE[insnOpcode]
+ visitEdge(insn, insn + 1, outputStackSize)
+ }
+
+ private fun visitInsn(insnOpcode: Int, insnStackSize: Int, insn: Int) {
+ if (insnOpcode != Opcodes.ATHROW && insnOpcode !in Opcodes.IRETURN..Opcodes.RETURN) {
+ val outputStackSize = insnStackSize + STACK_SIZE_CHANGE_BY_OPCODE[insnOpcode]
+ visitEdge(insn, insn + 1, outputStackSize)
+ }
+ }
+
+ private fun visitJumpInsn(insnNode: JumpInsnNode, insnStackSize: Int, insn: Int) {
+ val labelIndex = insnNode.label.indexOf()
+ val outputStackSize = insnStackSize + STACK_SIZE_CHANGE_BY_OPCODE[insnNode.opcode]
+ if (insnNode.opcode == Opcodes.GOTO) {
+ visitEdge(insn, labelIndex, insnStackSize)
+ } else {
+ // Conditional jump (IFEQ, IFLE, IFLT, etc)
+ visitEdge(insn, labelIndex, outputStackSize)
+ visitEdge(insn, insn + 1, outputStackSize)
+ }
+ }
+
+ private fun visitEdge(src: Int, dest: Int, newStackSize: Int) {
+ if (newStackSize < 0) {
+ throw AssertionError("Stack underflow at instruction #$src")
+ }
+ val destStackSize = stackSize[dest]
+ if (destStackSize < 0) {
+ stackSize[dest] = newStackSize
+ queue[top++] = dest
+ } else {
+ if (destStackSize != newStackSize) {
+ throw AssertionError("Stack size mismatch at instruction #$dest: $newStackSize != $destStackSize")
+ }
+ }
+ if (maxStack < newStackSize) {
+ maxStack = newStackSize
+ }
+ }
+
+ private fun AbstractInsnNode.indexOf() =
+ insnList.indexOf(this)
+}
diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt
index 176ff96..85dc468 100644
--- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt
+++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/Util.kt
@@ -16,12 +16,11 @@
package org.jetbrains.kotlin.codegen.optimization.common
-import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator
+import org.jetbrains.kotlin.codegen.FastFrameSizeCalculator
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
-import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes.*
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
@@ -89,15 +88,7 @@
fun MethodNode.updateMaxStack() {
maxStack = -1
- accept(
- MaxStackFrameSizeAndLocalsCalculator(
- API_VERSION, access, desc,
- object : MethodVisitor(API_VERSION) {
- override fun visitMaxs(maxStack: Int, maxLocals: Int) {
- this@updateMaxStack.maxStack = maxStack
- }
- })
- )
+ FastFrameSizeCalculator(this).updateMaxStack()
}
fun MethodNode.stripOptimizationMarkers() {