JVM KT-47613 custom control flow analyzer for CFG builder
This commit is contained in:
@@ -347,8 +347,42 @@ internal val AbstractInsnNode?.insnText: String
|
||||
return sw.toString().trim()
|
||||
}
|
||||
|
||||
fun AbstractInsnNode?.insnText(insnList: InsnList): String {
|
||||
if (this == null) return "<null>"
|
||||
|
||||
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
|
||||
|
||||
+144
-31
@@ -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<MutableList<Int>> = Array(insns.size()) { ArrayList() }
|
||||
private val backwardEdges: Array<MutableList<Int>> = Array(insns.size()) { ArrayList() }
|
||||
private val successors: Array<MutableList<Int>> = Array(insns.size()) { ArrayList() }
|
||||
private val predecessors: Array<MutableList<Int>> = Array(insns.size()) { ArrayList() }
|
||||
|
||||
fun getSuccessorsIndices(insn: AbstractInsnNode): List<Int> = getSuccessorsIndices(insns.indexOf(insn))
|
||||
fun getSuccessorsIndices(index: Int): List<Int> = edges[index]
|
||||
fun getSuccessorsIndices(index: Int): List<Int> = successors[index]
|
||||
fun getPredecessorsIndices(insn: AbstractInsnNode): List<Int> = getPredecessorsIndices(insns.indexOf(insn))
|
||||
fun getPredecessorsIndices(index: Int): List<Int> = backwardEdges[index]
|
||||
fun getPredecessorsIndices(index: Int): List<Int> = 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<MutableList<TryCatchBlockNode>?> = 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<TryCatchBlockNode>().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<BasicValue>("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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -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<V : Value>(
|
||||
|
||||
checkAssertions()
|
||||
|
||||
computeExceptionHandlersForEachInsn(method)
|
||||
computeExceptionHandlersForEachInsn()
|
||||
|
||||
initSinglePredBlocks()
|
||||
|
||||
@@ -306,8 +305,8 @@ open class FlexibleMethodAnalyzer<V : Value>(
|
||||
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) {
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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/)
|
||||
|
||||
Reference in New Issue
Block a user