mirror of
https://github.com/jlengrand/kotlin.git
synced 2026-03-10 08:31:29 +00:00
JVM use SPBs in fix stack analyzer
This commit is contained in:
committed by
teamcityserver
parent
38f45d2969
commit
d99d25e51e
@@ -31,7 +31,7 @@ class ControlFlowGraph private constructor(private val insns: InsnList) {
|
||||
}
|
||||
|
||||
// TODO custom analyzer, no need to analyze data flow
|
||||
object : MethodAnalyzer<BasicValue>("fake", node, OptimizationBasicInterpreter()) {
|
||||
object : FlexibleMethodAnalyzer<BasicValue>("fake", node, OptimizationBasicInterpreter()) {
|
||||
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
||||
addEdge(insn, successor)
|
||||
return true
|
||||
|
||||
@@ -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<V : Value>(
|
||||
private val owner: String,
|
||||
|
||||
@@ -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<V : Value>(
|
||||
@Suppress("DuplicatedCode")
|
||||
open class FlexibleMethodAnalyzer<V : Value>(
|
||||
private val owner: String,
|
||||
val method: MethodNode,
|
||||
protected val interpreter: Interpreter<V>
|
||||
) {
|
||||
val instructions: InsnList = method.instructions
|
||||
private val nInsns: Int = instructions.size()
|
||||
protected val insnsArray: Array<AbstractInsnNode> = method.instructions.toArray()
|
||||
private val nInsns = insnsArray.size
|
||||
|
||||
val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||
|
||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = 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<V> = Frame(nLocals, nStack)
|
||||
|
||||
protected open fun newFrame(src: Frame<out V>): Frame<V> {
|
||||
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<Frame<V>?> {
|
||||
if (nInsns == 0) return frames
|
||||
|
||||
@@ -100,6 +95,8 @@ open class MethodAnalyzer<V : Value>(
|
||||
|
||||
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<V : Value>(
|
||||
|
||||
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<V : Value>(
|
||||
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<V>? =
|
||||
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<V : Value>(
|
||||
}
|
||||
|
||||
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>, 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<V : Value>(
|
||||
// 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<V>, 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<V : Value>(
|
||||
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<V : Value>(
|
||||
|
||||
private fun processControlFlowEdge(current: Frame<V>, insn: Int, jump: Int) {
|
||||
if (visitControlFlowEdge(insn, jump)) {
|
||||
mergeControlFlowEdge(jump, current)
|
||||
mergeControlFlowEdge(insn, jump, current)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -225,16 +303,16 @@ open class MethodAnalyzer<V : Value>(
|
||||
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<TryCatchBlockNode>? = handlers[j]
|
||||
if (insnHandlers == null) {
|
||||
insnHandlers = ArrayList<TryCatchBlockNode>()
|
||||
@@ -245,18 +323,24 @@ open class MethodAnalyzer<V : Value>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) {
|
||||
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<V>) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
|
||||
private inner class InternalAnalyzer(owner: String) : FlexibleMethodAnalyzer<BasicValue>(owner, method, OptimizationBasicInterpreter()) {
|
||||
val spilledStacks = hashMapOf<AbstractInsnNode, List<BasicValue>>()
|
||||
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))
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user