JVM use SPBs in fix stack analyzer
This commit is contained in:
committed by
teamcityserver
parent
38f45d2969
commit
d99d25e51e
+1
-1
@@ -31,7 +31,7 @@ class ControlFlowGraph private constructor(private val insns: InsnList) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO custom analyzer, no need to analyze data flow
|
// 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 {
|
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
||||||
addEdge(insn, successor)
|
addEdge(insn, successor)
|
||||||
return true
|
return true
|
||||||
|
|||||||
+3
@@ -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.Interpreter
|
||||||
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see FlexibleMethodAnalyzer
|
||||||
|
*/
|
||||||
@Suppress("DuplicatedCode")
|
@Suppress("DuplicatedCode")
|
||||||
open class FastMethodAnalyzer<V : Value>(
|
open class FastMethodAnalyzer<V : Value>(
|
||||||
private val owner: String,
|
private val owner: String,
|
||||||
|
|||||||
+126
-42
@@ -58,41 +58,36 @@ import java.util.*
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* This class is a modified version of `org.objectweb.asm.tree.analysis.Analyzer`
|
* This class is a modified version of `org.objectweb.asm.tree.analysis.Analyzer`
|
||||||
|
*
|
||||||
|
* @see FastMethodAnalyzer
|
||||||
|
*
|
||||||
* @author Eric Bruneton
|
* @author Eric Bruneton
|
||||||
* @author Dmitry Petrov
|
* @author Dmitry Petrov
|
||||||
*/
|
*/
|
||||||
open class MethodAnalyzer<V : Value>(
|
@Suppress("DuplicatedCode")
|
||||||
|
open class FlexibleMethodAnalyzer<V : Value>(
|
||||||
private val owner: String,
|
private val owner: String,
|
||||||
val method: MethodNode,
|
val method: MethodNode,
|
||||||
protected val interpreter: Interpreter<V>
|
protected val interpreter: Interpreter<V>
|
||||||
) {
|
) {
|
||||||
val instructions: InsnList = method.instructions
|
protected val insnsArray: Array<AbstractInsnNode> = method.instructions.toArray()
|
||||||
private val nInsns: Int = instructions.size()
|
private val nInsns = insnsArray.size
|
||||||
|
|
||||||
val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
|
||||||
|
|
||||||
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
|
||||||
private val queued: BooleanArray = BooleanArray(nInsns)
|
private val queued = BooleanArray(nInsns)
|
||||||
private val queue: IntArray = IntArray(nInsns)
|
private val queue = IntArray(nInsns)
|
||||||
private var top: Int = 0
|
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(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 visitControlFlowEdge(insn: Int, successor: Int): Boolean = true
|
||||||
|
|
||||||
protected open fun visitControlFlowExceptionEdge(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>?> {
|
fun analyze(): Array<Frame<V>?> {
|
||||||
if (nInsns == 0) return frames
|
if (nInsns == 0) return frames
|
||||||
|
|
||||||
@@ -100,6 +95,8 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
|
|
||||||
computeExceptionHandlersForEachInsn(method)
|
computeExceptionHandlersForEachInsn(method)
|
||||||
|
|
||||||
|
initSinglePredBlocks()
|
||||||
|
|
||||||
val current = newFrame(method.maxLocals, method.maxStack)
|
val current = newFrame(method.maxLocals, method.maxStack)
|
||||||
val handler = newFrame(method.maxLocals, method.maxStack)
|
val handler = newFrame(method.maxLocals, method.maxStack)
|
||||||
initControlFlowAnalysis(current, method, owner)
|
initControlFlowAnalysis(current, method, owner)
|
||||||
@@ -135,12 +132,12 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
|
|
||||||
handlers[insn]?.forEach { tcb ->
|
handlers[insn]?.forEach { tcb ->
|
||||||
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
|
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
|
||||||
val jump = instructions.indexOf(tcb.handler)
|
val jump = tcb.handler.indexOf()
|
||||||
if (visitControlFlowExceptionEdge(insn, tcb)) {
|
if (visitControlFlowExceptionEdge(insn, tcb.handler.indexOf())) {
|
||||||
handler.init(f)
|
handler.init(f)
|
||||||
handler.clearStack()
|
handler.clearStack()
|
||||||
handler.push(interpreter.newValue(exnType))
|
handler.push(interpreter.newValue(exnType))
|
||||||
mergeControlFlowEdge(jump, handler)
|
mergeControlFlowEdge(insn, jump, handler)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,11 +152,92 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
return frames
|
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>? =
|
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
|
||||||
frames[instructions.indexOf(insn)]
|
frames[insn.indexOf()]
|
||||||
|
|
||||||
private fun checkAssertions() {
|
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")
|
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) {
|
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)
|
processControlFlowEdge(current, insn, jump)
|
||||||
// In most cases order of visiting switch labels should not matter
|
// 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
|
// 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
|
// 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
|
// Using 'reversed' is because nodes are processed in LIFO order
|
||||||
for (label in insnNode.labels.reversed()) {
|
for (label in insnNode.labels.reversed()) {
|
||||||
jump = instructions.indexOf(label)
|
jump = label.indexOf()
|
||||||
processControlFlowEdge(current, insn, jump)
|
processControlFlowEdge(current, insn, jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>, insn: Int) {
|
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)
|
processControlFlowEdge(current, insn, jump)
|
||||||
for (label in insnNode.labels) {
|
for (label in insnNode.labels) {
|
||||||
jump = instructions.indexOf(label)
|
jump = label.indexOf()
|
||||||
processControlFlowEdge(current, insn, jump)
|
processControlFlowEdge(current, insn, jump)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,7 +272,7 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
|
if (insnOpcode != Opcodes.GOTO && insnOpcode != Opcodes.JSR) {
|
||||||
processControlFlowEdge(current, insn, insn + 1)
|
processControlFlowEdge(current, insn, insn + 1)
|
||||||
}
|
}
|
||||||
val jump = instructions.indexOf(insnNode.label)
|
val jump = insnNode.label.indexOf()
|
||||||
processControlFlowEdge(current, insn, jump)
|
processControlFlowEdge(current, insn, jump)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -204,7 +282,7 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
|
|
||||||
private fun processControlFlowEdge(current: Frame<V>, insn: Int, jump: Int) {
|
private fun processControlFlowEdge(current: Frame<V>, insn: Int, jump: Int) {
|
||||||
if (visitControlFlowEdge(insn, jump)) {
|
if (visitControlFlowEdge(insn, jump)) {
|
||||||
mergeControlFlowEdge(jump, current)
|
mergeControlFlowEdge(insn, jump, current)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,16 +303,16 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
while (local < m.maxLocals) {
|
while (local < m.maxLocals) {
|
||||||
current.setLocal(local++, interpreter.newValue(null))
|
current.setLocal(local++, interpreter.newValue(null))
|
||||||
}
|
}
|
||||||
mergeControlFlowEdge(0, current)
|
mergeControlFlowEdge(0, 0, current)
|
||||||
|
|
||||||
init(owner, m)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
private fun computeExceptionHandlersForEachInsn(m: MethodNode) {
|
||||||
for (tcb in m.tryCatchBlocks) {
|
for (tcb in m.tryCatchBlocks) {
|
||||||
val begin = instructions.indexOf(tcb.start)
|
val begin = tcb.start.indexOf()
|
||||||
val end = instructions.indexOf(tcb.end)
|
val end = tcb.end.indexOf()
|
||||||
for (j in begin until end) {
|
for (j in begin until end) {
|
||||||
|
val insn = insnsArray[j]
|
||||||
|
if (!insn.isMeaningful) continue
|
||||||
var insnHandlers: MutableList<TryCatchBlockNode>? = handlers[j]
|
var insnHandlers: MutableList<TryCatchBlockNode>? = handlers[j]
|
||||||
if (insnHandlers == null) {
|
if (insnHandlers == null) {
|
||||||
insnHandlers = ArrayList<TryCatchBlockNode>()
|
insnHandlers = ArrayList<TryCatchBlockNode>()
|
||||||
@@ -245,18 +323,24 @@ open class MethodAnalyzer<V : Value>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) {
|
private fun mergeControlFlowEdge(src: Int, dest: Int, frame: Frame<V>) {
|
||||||
val oldFrame = frames[insn]
|
val oldFrame = frames[dest]
|
||||||
val changes =
|
val changes = when {
|
||||||
if (oldFrame != null)
|
oldFrame == null -> {
|
||||||
oldFrame.merge(frame, interpreter)
|
frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) }
|
||||||
else {
|
|
||||||
frames[insn] = newFrame(frame)
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
if (changes && !queued[insn]) {
|
dest == src + 1 && singlePredBlock[src] == singlePredBlock[dest] -> {
|
||||||
queued[insn] = true
|
// Forward jump within a single predecessor block, no need to merge.
|
||||||
queue[top++] = insn
|
oldFrame.init(frame)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
else ->
|
||||||
|
oldFrame.merge(frame, interpreter)
|
||||||
|
}
|
||||||
|
if (changes && !queued[dest]) {
|
||||||
|
queued[dest] = true
|
||||||
|
queue[top++] = dest
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
+3
-3
@@ -20,7 +20,7 @@ import com.intellij.util.containers.Stack
|
|||||||
import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
|
import org.jetbrains.kotlin.codegen.inline.isAfterInlineMarker
|
||||||
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
|
import org.jetbrains.kotlin.codegen.inline.isBeforeInlineMarker
|
||||||
import org.jetbrains.kotlin.codegen.inline.isMarkedReturn
|
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.optimization.common.OptimizationBasicInterpreter
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
|
||||||
import org.jetbrains.kotlin.utils.SmartList
|
import org.jetbrains.kotlin.utils.SmartList
|
||||||
@@ -88,13 +88,13 @@ internal class FixStackAnalyzer(
|
|||||||
|
|
||||||
private val analyzer = InternalAnalyzer(owner)
|
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>>()
|
val spilledStacks = hashMapOf<AbstractInsnNode, List<BasicValue>>()
|
||||||
var maxExtraStackSize = 0; private set
|
var maxExtraStackSize = 0; private set
|
||||||
|
|
||||||
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
override fun visitControlFlowEdge(insn: Int, successor: Int): Boolean {
|
||||||
if (!skipBreakContinueGotoEdges) return true
|
if (!skipBreakContinueGotoEdges) return true
|
||||||
val insnNode = instructions[insn]
|
val insnNode = insnsArray[insn]
|
||||||
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
|
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ the Kotlin IntelliJ IDEA plugin:
|
|||||||
- License: BSD ([license/third_party/asm_license.txt][asm])
|
- 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
|
- 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])
|
- 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
|
- 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