diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt index 53dacdb752a..584ce2a70c2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/inlineCodegenUtils.kt @@ -347,8 +347,42 @@ internal val AbstractInsnNode?.insnText: String return sw.toString().trim() } +fun AbstractInsnNode?.insnText(insnList: InsnList): String { + if (this == null) return "" + + fun AbstractInsnNode.indexOf() = + insnList.indexOf(this) + + fun LabelNode.labelText() = + "L#${this.indexOf()}" + + return when (this) { + is LabelNode -> + labelText() + is JumpInsnNode -> + "$insnOpcodeText ${label.labelText()}" + is LookupSwitchInsnNode -> + "$insnOpcodeText " + + this.keys.zip(this.labels).joinToString(prefix = "[", postfix = "]") { (key, label) -> "$key:${label.labelText()}" } + is TableSwitchInsnNode -> + "$insnOpcodeText " + + (min..max).zip(this.labels).joinToString(prefix = "[", postfix = "]") { (key, label) -> "$key:${label.labelText()}" } + else -> + insnText + } +} + internal val AbstractInsnNode?.insnOpcodeText: String - get() = if (this == null) "null" else Printer.OPCODES[opcode] + get() = when (this) { + null -> "null" + is LabelNode -> "LABEL" + is LineNumberNode -> "LINENUMBER" + is FrameNode -> "FRAME" + else -> Printer.OPCODES[opcode] + } + +internal fun TryCatchBlockNode.text(insns: InsnList): String = + "[${insns.indexOf(start)} .. ${insns.indexOf(end)} -> ${insns.indexOf(handler)}]" internal fun loadClassBytesByInternalName(state: GenerationState, internalName: String): ByteArray { //try to find just compiled classes then in dependencies 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 fbade5db047..b2bbc2e1c05 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 @@ -5,47 +5,160 @@ package org.jetbrains.kotlin.codegen.optimization.common -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.InsnList -import org.jetbrains.org.objectweb.asm.tree.MethodNode -import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue +import gnu.trove.TIntHashSet +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.tree.* class ControlFlowGraph private constructor(private val insns: InsnList) { - private val edges: Array> = Array(insns.size()) { ArrayList() } - private val backwardEdges: Array> = Array(insns.size()) { ArrayList() } + private val successors: Array> = Array(insns.size()) { ArrayList() } + private val predecessors: Array> = Array(insns.size()) { ArrayList() } fun getSuccessorsIndices(insn: AbstractInsnNode): List = getSuccessorsIndices(insns.indexOf(insn)) - fun getSuccessorsIndices(index: Int): List = edges[index] + fun getSuccessorsIndices(index: Int): List = successors[index] fun getPredecessorsIndices(insn: AbstractInsnNode): List = getPredecessorsIndices(insns.indexOf(insn)) - fun getPredecessorsIndices(index: Int): List = backwardEdges[index] + fun getPredecessorsIndices(index: Int): List = predecessors[index] + + private class Builder( + private val method: MethodNode, + private val followExceptions: Boolean + ) { + private val instructions = method.instructions + private val nInsns = instructions.size() + + private val handlers: Array?> = arrayOfNulls(nInsns) + + private val queued = BooleanArray(nInsns) + private val queue = IntArray(nInsns) + private var top = 0 + + private val predecessors = Array(nInsns) { TIntHashSet() } + + private val AbstractInsnNode.indexOf get() = instructions.indexOf(this) + + fun build(): ControlFlowGraph { + val graph = ControlFlowGraph(method.instructions) + if (nInsns == 0) return graph + + checkAssertions() + computeExceptionHandlersForEachInsn() + initControlFlowAnalysis() + traverseCfg() + + for ((i, preds) in predecessors.withIndex()) { + for (pred in preds.toArray()) { + graph.predecessors[i].add(pred) + graph.successors[pred].add(i) + } + } + return graph + } + + private fun traverseCfg() { + while (top > 0) { + val insn = queue[--top] + val insnNode = method.instructions[insn] + val insnOpcode = insnNode.opcode + + when (insnNode.type) { + AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> + visitOpInsn(insn) + AbstractInsnNode.JUMP_INSN -> + visitJumpInsnNode(insnNode as JumpInsnNode, insn, insnOpcode) + AbstractInsnNode.LOOKUPSWITCH_INSN -> + visitLookupSwitchInsnNode(insn, insnNode as LookupSwitchInsnNode) + AbstractInsnNode.TABLESWITCH_INSN -> + visitTableSwitchInsnNode(insn, insnNode as TableSwitchInsnNode) + else -> { + if (insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN)) { + visitOpInsn(insn) + } + } + } + + + handlers[insn]?.forEach { tcb -> + visitExceptionEdge(insn, tcb.handler.indexOf) + } + } + } + + private fun checkAssertions() { + if (instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) + throw AssertionError("Subroutines are deprecated since Java 6") + } + + private fun visitOpInsn(insn: Int) { + visitEdge(insn, insn + 1) + } + + private fun visitTableSwitchInsnNode(insn: Int, insnNode: TableSwitchInsnNode) { + var jump = insnNode.dflt.indexOf + visitEdge(insn, jump) + for (label in insnNode.labels) { + jump = label.indexOf + visitEdge(insn, jump) + } + } + + private fun visitLookupSwitchInsnNode(insn: Int, insnNode: LookupSwitchInsnNode) { + var jump = insnNode.dflt.indexOf + visitEdge(insn, jump) + for (label in insnNode.labels) { + jump = label.indexOf + visitEdge(insn, jump) + } + } + + private fun visitJumpInsnNode(insnNode: JumpInsnNode, insn: Int, insnOpcode: Int) { + if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) { + visitEdge(insn, insn + 1) + } + val jump = insnNode.label.indexOf + visitEdge(insn, jump) + } + + private fun initControlFlowAnalysis() { + queued[0] = true + queue[top++] = 0 + } + + private fun computeExceptionHandlersForEachInsn() { + for (tcb in method.tryCatchBlocks) { + val begin = tcb.start.indexOf + val end = tcb.end.indexOf + for (j in begin until end) { + val insnHandlers = handlers[j] + ?: ArrayList().also { handlers[j] = it } + insnHandlers.add(tcb) + } + } + } + + private fun visitExceptionEdge(from: Int, to: Int) { + if (followExceptions) { + predecessors[to].add(from) + } + enqueue(to) + } + + private fun visitEdge(from: Int, to: Int) { + predecessors[to].add(from) + enqueue(to) + } + + private fun enqueue(insn: Int) { + if (!queued[insn]) { + queued[insn] = true + queue[top++] = insn + } + } + } companion object { @JvmStatic fun build(node: MethodNode, followExceptions: Boolean = true): ControlFlowGraph { - val graph = ControlFlowGraph(node.instructions) - - fun addEdge(from: Int, to: Int) { - graph.edges[from].add(to) - graph.backwardEdges[to].add(from) - } - - // TODO custom analyzer, no need to analyze data flow - object : FlexibleMethodAnalyzer("fake", node, OptimizationBasicInterpreter()) { - override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean { - addEdge(insn, successor) - return true - } - - override fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean { - if (followExceptions) { - addEdge(insn, successor) - } - return true - } - }.analyze() - - return graph + return Builder(node, followExceptions).build() } } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt index 7ed6cd6afd6..5b0e567d941 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/FlexibleMethodAnalyzer.kt @@ -54,7 +54,6 @@ 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 -import java.util.* /** * This class is a modified version of `org.objectweb.asm.tree.analysis.Analyzer` @@ -93,7 +92,7 @@ open class FlexibleMethodAnalyzer( checkAssertions() - computeExceptionHandlersForEachInsn(method) + computeExceptionHandlersForEachInsn() initSinglePredBlocks() @@ -306,8 +305,8 @@ open class FlexibleMethodAnalyzer( mergeControlFlowEdge(0, 0, current) } - private fun computeExceptionHandlersForEachInsn(m: MethodNode) { - for (tcb in m.tryCatchBlocks) { + private fun computeExceptionHandlersForEachInsn() { + for (tcb in method.tryCatchBlocks) { val begin = tcb.start.indexOf() val end = tcb.end.indexOf() for (j in begin until end) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/InstructionLivenessAnalyzer.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/InstructionLivenessAnalyzer.kt index 6d99cd39cf8..a15e04f870f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/InstructionLivenessAnalyzer.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/optimization/common/InstructionLivenessAnalyzer.kt @@ -124,7 +124,7 @@ class InstructionLivenessAnalyzer(val method: MethodNode) { var jump = insnNode.dflt.indexOf visitControlFlowEdge(jump) for (label in insnNode.labels) { - jump = instructions.indexOf(label) + jump = label.indexOf visitControlFlowEdge(jump) } } diff --git a/license/README.md b/license/README.md index a8ec4920814..f1c03c6fd05 100644 --- a/license/README.md +++ b/license/README.md @@ -27,6 +27,10 @@ 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/ControlFlowGraph.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 + - Path: core/reflection.jvm/src/kotlin.reflect/jvm/internal/pcollections - License: MIT ([license/third_party/pcollections_LICENSE.txt][pcollections]) - Origin: Derived from PCollections, A Persistent Java Collections Library (https://pcollections.org/)