From d99d25e51e7a428db34af003676ddd692a4b2198 Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Sat, 10 Jul 2021 12:27:05 +0300 Subject: [PATCH] JVM use SPBs in fix stack analyzer --- .../optimization/common/ControlFlowGraph.kt | 2 +- .../optimization/common/FastMethodAnalyzer.kt | 3 + ...dAnalyzer.kt => FlexibleMethodAnalyzer.kt} | 168 +++++++++++++----- .../optimization/fixStack/FixStackAnalyzer.kt | 6 +- license/README.md | 2 +- 5 files changed, 134 insertions(+), 47 deletions(-) rename compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/{MethodAnalyzer.kt => FlexibleMethodAnalyzer.kt} (65%) diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/ControlFlowGraph.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/ControlFlowGraph.kt index 3a13567066f..fbade5db047 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/ControlFlowGraph.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/ControlFlowGraph.kt @@ -31,7 +31,7 @@ class ControlFlowGraph private constructor(private val insns: InsnList) { } // TODO custom analyzer, no need to analyze data flow - object : MethodAnalyzer("fake", node, OptimizationBasicInterpreter()) { + object : FlexibleMethodAnalyzer("fake", node, OptimizationBasicInterpreter()) { override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean { addEdge(insn, successor) return 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 fb4c3647e46..f7f850cff27 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 @@ -44,6 +44,9 @@ 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 +/** + * @see FlexibleMethodAnalyzer + */ @Suppress("DuplicatedCode") open class FastMethodAnalyzer( private val owner: String, diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/MethodAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt similarity index 65% rename from compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/MethodAnalyzer.kt rename to compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt index 4b0b0defabe..7ed6cd6afd6 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/MethodAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt @@ -58,41 +58,36 @@ import java.util.* /** * This class is a modified version of `org.objectweb.asm.tree.analysis.Analyzer` + * + * @see FastMethodAnalyzer + * * @author Eric Bruneton * @author Dmitry Petrov */ -open class MethodAnalyzer( +@Suppress("DuplicatedCode") +open class FlexibleMethodAnalyzer( private val owner: String, val method: MethodNode, protected val interpreter: Interpreter ) { - val instructions: InsnList = method.instructions - private val nInsns: Int = instructions.size() + protected val insnsArray: Array = method.instructions.toArray() + private val nInsns = insnsArray.size val frames: Array?> = arrayOfNulls(nInsns) private val handlers: Array?> = arrayOfNulls(nInsns) - private val queued: BooleanArray = BooleanArray(nInsns) - private val queue: IntArray = IntArray(nInsns) - private var top: Int = 0 + private val queued = BooleanArray(nInsns) + private val queue = IntArray(nInsns) + private var top = 0 - protected open fun init(owner: String, m: MethodNode) {} + private val singlePredBlock = IntArray(nInsns) protected open fun newFrame(nLocals: Int, nStack: Int): Frame = Frame(nLocals, nStack) - protected open fun newFrame(src: Frame): Frame { - val frame = newFrame(src.locals, src.maxStackSize) - frame.init(src) - return frame - } - protected open fun visitControlFlowEdge(insn: Int, successor: Int): Boolean = true protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true - protected open fun visitControlFlowExceptionEdge(insn: Int, tcb: TryCatchBlockNode): Boolean = - visitControlFlowExceptionEdge(insn, instructions.indexOf(tcb.handler)) - fun analyze(): Array?> { if (nInsns == 0) return frames @@ -100,6 +95,8 @@ open class MethodAnalyzer( computeExceptionHandlersForEachInsn(method) + initSinglePredBlocks() + val current = newFrame(method.maxLocals, method.maxStack) val handler = newFrame(method.maxLocals, method.maxStack) initControlFlowAnalysis(current, method, owner) @@ -135,12 +132,12 @@ open class MethodAnalyzer( handlers[insn]?.forEach { tcb -> val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") - val jump = instructions.indexOf(tcb.handler) - if (visitControlFlowExceptionEdge(insn, tcb)) { + val jump = tcb.handler.indexOf() + if (visitControlFlowExceptionEdge(insn, tcb.handler.indexOf())) { handler.init(f) handler.clearStack() handler.push(interpreter.newValue(exnType)) - mergeControlFlowEdge(jump, handler) + mergeControlFlowEdge(insn, jump, handler) } } @@ -155,11 +152,92 @@ open class MethodAnalyzer( return frames } + private fun initSinglePredBlocks() { + markSinglePredBlockEntries() + markSinglePredBlockBodies() + } + + private fun markSinglePredBlockEntries() { + // Method entry point is SPB entry point. + var blockId = 0 + singlePredBlock[0] = ++blockId + + // Every jump target is SPB entry point. + for (insn in insnsArray) { + when (insn) { + is JumpInsnNode -> { + val labelIndex = insn.label.indexOf() + if (singlePredBlock[labelIndex] == 0) { + singlePredBlock[labelIndex] = ++blockId + } + } + is LookupSwitchInsnNode -> { + insn.dflt?.let { dfltLabel -> + val dfltIndex = dfltLabel.indexOf() + if (singlePredBlock[dfltIndex] == 0) { + singlePredBlock[dfltIndex] = ++blockId + } + } + for (label in insn.labels) { + val labelIndex = label.indexOf() + if (singlePredBlock[labelIndex] == 0) { + singlePredBlock[labelIndex] = ++blockId + } + } + } + is TableSwitchInsnNode -> { + insn.dflt?.let { dfltLabel -> + val dfltIndex = dfltLabel.indexOf() + if (singlePredBlock[dfltIndex] == 0) { + singlePredBlock[dfltIndex] = ++blockId + } + } + for (label in insn.labels) { + val labelIndex = label.indexOf() + if (singlePredBlock[labelIndex] == 0) { + singlePredBlock[labelIndex] = ++blockId + } + } + } + } + } + + // Every try-catch block handler entry point is SPB entry point + for (tcb in method.tryCatchBlocks) { + val handlerIndex = tcb.handler.indexOf() + if (singlePredBlock[handlerIndex] == 0) { + singlePredBlock[handlerIndex] = ++blockId + } + } + } + + private fun markSinglePredBlockBodies() { + var current = 0 + for ((i, insn) in insnsArray.withIndex()) { + if (singlePredBlock[i] == 0) { + singlePredBlock[i] = current + } else { + // Entered a new SPB. + current = singlePredBlock[i] + } + + // GOTO, ATHROW, *RETURN instructions terminate current SPB. + when (insn.opcode) { + Opcodes.GOTO, + Opcodes.ATHROW, + in Opcodes.IRETURN..Opcodes.RETURN -> + current = 0 + } + } + } + + private fun AbstractInsnNode.indexOf() = method.instructions.indexOf(this) + fun getFrame(insn: AbstractInsnNode): Frame? = - frames[instructions.indexOf(insn)] + frames[insn.indexOf()] private fun checkAssertions() { - if (instructions.toArray().any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) + if (insnsArray.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) throw AssertionError("Subroutines are deprecated since Java 6") } @@ -168,7 +246,7 @@ open class MethodAnalyzer( } private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame, insn: Int) { - var jump = instructions.indexOf(insnNode.dflt) + var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insn, jump) // 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 @@ -176,16 +254,16 @@ open class MethodAnalyzer( // So we just fix the order of labels being traversed: the first one should be one at the method beginning // Using 'reversed' is because nodes are processed in LIFO order for (label in insnNode.labels.reversed()) { - jump = instructions.indexOf(label) + jump = label.indexOf() processControlFlowEdge(current, insn, jump) } } private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame, insn: Int) { - var jump = instructions.indexOf(insnNode.dflt) + var jump = insnNode.dflt.indexOf() processControlFlowEdge(current, insn, jump) for (label in insnNode.labels) { - jump = instructions.indexOf(label) + jump = label.indexOf() processControlFlowEdge(current, insn, jump) } } @@ -194,7 +272,7 @@ open class MethodAnalyzer( if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) { processControlFlowEdge(current, insn, insn + 1) } - val jump = instructions.indexOf(insnNode.label) + val jump = insnNode.label.indexOf() processControlFlowEdge(current, insn, jump) } @@ -204,7 +282,7 @@ open class MethodAnalyzer( private fun processControlFlowEdge(current: Frame, insn: Int, jump: Int) { if (visitControlFlowEdge(insn, jump)) { - mergeControlFlowEdge(jump, current) + mergeControlFlowEdge(insn, jump, current) } } @@ -225,16 +303,16 @@ open class MethodAnalyzer( while (local < m.maxLocals) { current.setLocal(local++, interpreter.newValue(null)) } - mergeControlFlowEdge(0, current) - - init(owner, m) + mergeControlFlowEdge(0, 0, current) } private fun computeExceptionHandlersForEachInsn(m: MethodNode) { for (tcb in m.tryCatchBlocks) { - val begin = instructions.indexOf(tcb.start) - val end = instructions.indexOf(tcb.end) + val begin = tcb.start.indexOf() + val end = tcb.end.indexOf() for (j in begin until end) { + val insn = insnsArray[j] + if (!insn.isMeaningful) continue var insnHandlers: MutableList? = handlers[j] if (insnHandlers == null) { insnHandlers = ArrayList() @@ -245,18 +323,24 @@ open class MethodAnalyzer( } } - private fun mergeControlFlowEdge(insn: Int, frame: Frame) { - val oldFrame = frames[insn] - val changes = - if (oldFrame != null) - oldFrame.merge(frame, interpreter) - else { - frames[insn] = newFrame(frame) + private fun mergeControlFlowEdge(src: Int, dest: Int, frame: Frame) { + val oldFrame = frames[dest] + val changes = when { + oldFrame == null -> { + frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) } true } - if (changes && !queued[insn]) { - queued[insn] = true - queue[top++] = insn + dest == src + 1 && singlePredBlock[src] == singlePredBlock[dest] -> { + // Forward jump within a single predecessor block, no need to merge. + oldFrame.init(frame) + true + } + else -> + oldFrame.merge(frame, interpreter) + } + if (changes && !queued[dest]) { + queued[dest] = true + queue[top++] = dest } } 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 a554bf94c67..755497e209a 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 @@ -20,7 +20,7 @@ import com.intellij.util.containers.Stack import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker import org.jetbrains.kotlin.codegen.inline.isMarkedReturn -import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer +import org.jetbrains.kotlin.codegen.optimization.common.FlexibleMethodAnalyzer import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.utils.SmartList @@ -88,13 +88,13 @@ internal class FixStackAnalyzer( private val analyzer = InternalAnalyzer(owner) - private inner class InternalAnalyzer(owner: String) : MethodAnalyzer(owner, method, OptimizationBasicInterpreter()) { + private inner class InternalAnalyzer(owner: String) : FlexibleMethodAnalyzer(owner, method, OptimizationBasicInterpreter()) { val spilledStacks = hashMapOf>() var maxExtraStackSize = 0; private set override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean { if (!skipBreakContinueGotoEdges) return true - val insnNode = instructions[insn] + val insnNode = insnsArray[insn] return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode)) } diff --git a/license/README.md b/license/README.md index fda668bc575..a8ec4920814 100644 --- a/license/README.md +++ b/license/README.md @@ -15,7 +15,7 @@ the Kotlin IntelliJ IDEA plugin: - License: BSD ([license/third_party/asm_license.txt][asm]) - Origin: Derived from ASM: a very small and fast Java bytecode manipulation framework, Copyright (c) 2000-2011 INRIA, France Telecom - - Path: compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/MethodAnalyzer.kt + - Path: compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt - License: BSD ([license/third_party/asm_license.txt][asm]) - Origin: Derived from ASM: a very small and fast Java bytecode manipulation framework, Copyright (c) 2000-2011 INRIA, France Telecom