[JVM] Extract common code from Fast...Analyzer into analyzeInner

This commit is contained in:
Ivan Kylchik
2023-08-24 11:52:44 +02:00
committed by Space Team
parent e0dd3a33c0
commit abe727fdd7
5 changed files with 145 additions and 177 deletions
@@ -5,14 +5,16 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.utils.SmartList import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
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.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
abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F: Frame<V>>( abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F : Frame<V>>(
protected val owner: String, protected val owner: String,
protected val method: MethodNode, protected val method: MethodNode,
protected val interpreter: I, protected val interpreter: I,
@@ -20,6 +22,75 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F: Frame<V>>(
protected val nInsns = method.instructions.size() protected val nInsns = method.instructions.size()
protected val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns) protected val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
private var top = 0
protected fun analyzeInner() {
val current = newFrame(method.maxLocals, method.maxStack)
val handler = newFrame(method.maxLocals, method.maxStack)
initLocals(current)
mergeControlFlowEdge(0, current)
while (top > 0) {
val insn = queue[--top]
@Suppress("UNCHECKED_CAST")
val f = frames[insn]!! as F
queued[insn] = false
val insnNode = method.instructions[insn]
val insnOpcode = insnNode.opcode
val insnType = insnNode.type
try {
analyzeInstruction(insnNode, insn, insnType, insnOpcode, f, current, handler)
} catch (e: AnalyzerException) {
throw AnalyzerException(
e.node,
"Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}",
e
)
} catch (e: Exception) {
throw AnalyzerException(
insnNode,
"Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}",
e
)
}
}
}
protected abstract fun initLocals(current: F)
protected abstract fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean = false)
protected abstract fun analyzeInstruction(
insnNode: AbstractInsnNode,
insnIndex: Int,
insnType: Int,
insnOpcode: Int,
currentlyAnalyzing: F,
current: F,
handler: F
)
protected abstract fun newFrame(nLocals: Int, nStack: Int): F
@Suppress("UNCHECKED_CAST")
fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F
@Suppress("UNCHECKED_CAST")
protected fun getFrame(index: Int): F? = frames[index] as? F
protected fun setFrame(index: Int, newFrame: F) {
frames[index] = newFrame
}
protected fun getFrames(): Array<Frame<V>?> = frames
protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) { protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) {
when { when {
insnType == AbstractInsnNode.JUMP_INSN -> insnType == AbstractInsnNode.JUMP_INSN ->
@@ -82,6 +153,13 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F: Frame<V>>(
insnHandlers.add(tcb) insnHandlers.add(tcb)
} }
protected fun updateQueue(changes: Boolean, dest: Int) {
if (changes && !queued[dest]) {
queued[dest] = true
queue[top++] = dest
}
}
protected fun F.dump(): String { protected fun F.dump(): String {
return buildString { return buildString {
append("{\n") append("{\n")
@@ -33,7 +33,6 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
@@ -53,68 +52,33 @@ open class FastMethodAnalyzer<V : Value>
private val pruneExceptionEdges: Boolean = false private val pruneExceptionEdges: Boolean = false
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) { ) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
private val isMergeNode = findMergeNodes(method) private val isMergeNode = findMergeNodes(method)
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns) private val isTcbStart = BooleanArray(nInsns)
private val queued = BooleanArray(nInsns) override fun newFrame(nLocals: Int, nStack: Int): Frame<V> =
private val queue = IntArray(nInsns)
private var top = 0
protected open fun newFrame(nLocals: Int, nStack: Int): Frame<V> =
Frame(nLocals, nStack) Frame(nLocals, nStack)
fun analyze(): Array<Frame<V>?> { fun analyze(): Array<Frame<V>?> {
if (nInsns == 0) return frames if (nInsns == 0) return getFrames()
checkAssertions() checkAssertions()
computeExceptionHandlers(method) computeExceptionHandlers(method)
val isTcbStart = BooleanArray(nInsns)
for (tcb in method.tryCatchBlocks) { for (tcb in method.tryCatchBlocks) {
isTcbStart[tcb.start.indexOf() + 1] = true isTcbStart[tcb.start.indexOf() + 1] = true
} }
val current = newFrame(method.maxLocals, method.maxStack) analyzeInner()
val handler = newFrame(method.maxLocals, method.maxStack)
initLocals(current)
mergeControlFlowEdge(0, current)
while (top > 0) { return getFrames()
val insn = queue[--top]
val f = frames[insn]!!
queued[insn] = false
val insnNode = method.instructions[insn]
val insnOpcode = insnNode.opcode
val insnType = insnNode.type
try {
analyzeInstruction(insnType, insnOpcode, insn, f, current, insnNode, isTcbStart, handler)
} catch (e: AnalyzerException) {
throw AnalyzerException(
e.node,
"Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}",
e
)
} catch (e: Exception) {
throw AnalyzerException(
insnNode,
"Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}\ncurrent: ${current.dump()}",
e
)
}
}
return frames
} }
private fun analyzeInstruction( override fun analyzeInstruction(
insnNode: AbstractInsnNode,
insnIndex: Int,
insnType: Int, insnType: Int,
insnOpcode: Int, insnOpcode: Int,
insn: Int, currentlyAnalyzing: Frame<V>,
f: Frame<V>,
current: Frame<V>, current: Frame<V>,
insnNode: AbstractInsnNode,
isTcbStart: BooleanArray,
handler: Frame<V>, handler: Frame<V>,
) { ) {
if (insnType == AbstractInsnNode.LABEL || if (insnType == AbstractInsnNode.LABEL ||
@@ -122,10 +86,10 @@ open class FastMethodAnalyzer<V : Value>
insnType == AbstractInsnNode.FRAME || insnType == AbstractInsnNode.FRAME ||
insnOpcode == Opcodes.NOP insnOpcode == Opcodes.NOP
) { ) {
mergeControlFlowEdge(insn + 1, f, canReuse = true) mergeControlFlowEdge(insnIndex + 1, currentlyAnalyzing, canReuse = true)
} else { } else {
current.init(f).execute(insnNode, interpreter) current.init(currentlyAnalyzing).execute(insnNode, interpreter)
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex)
} }
// Jump by an exception edge clears the stack, putting exception on top. // Jump by an exception edge clears the stack, putting exception on top.
@@ -135,13 +99,13 @@ open class FastMethodAnalyzer<V : Value>
if (!pruneExceptionEdges || if (!pruneExceptionEdges ||
insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE || insnOpcode in Opcodes.ISTORE..Opcodes.ASTORE ||
insnOpcode == Opcodes.IINC || insnOpcode == Opcodes.IINC ||
isTcbStart[insn] isTcbStart[insnIndex]
) { ) {
handlers[insn]?.forEach { tcb -> handlers[insnIndex]?.forEach { tcb ->
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
val jump = tcb.handler.indexOf() val jump = tcb.handler.indexOf()
handler.init(f) handler.init(currentlyAnalyzing)
handler.clearStack() handler.clearStack()
handler.push(interpreter.newExceptionValue(tcb, handler, exnType)) handler.push(interpreter.newExceptionValue(tcb, handler, exnType))
mergeControlFlowEdge(jump, handler) mergeControlFlowEdge(jump, handler)
@@ -149,7 +113,7 @@ open class FastMethodAnalyzer<V : Value>
} }
} }
private fun initLocals(current: Frame<V>) { override fun initLocals(current: Frame<V>) {
current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc))) current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)))
val args = Type.getArgumentTypes(method.desc) val args = Type.getArgumentTypes(method.desc)
var local = 0 var local = 0
@@ -172,9 +136,6 @@ open class FastMethodAnalyzer<V : Value>
} }
} }
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
frames[insn.indexOf()]
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) { override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
mergeControlFlowEdge(insn + 1, current) mergeControlFlowEdge(insn + 1, current)
} }
@@ -210,15 +171,15 @@ open class FastMethodAnalyzer<V : Value>
* Reuses old frame when possible and when [canReuse] is true. * Reuses old frame when possible and when [canReuse] is true.
* If updated, adds the frame to the queue * If updated, adds the frame to the queue
*/ */
private fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean = false) { override fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean) {
val oldFrame = frames[dest] val oldFrame = getFrame(dest)
val changes = when { val changes = when {
canReuse && !isMergeNode[dest] -> { canReuse && !isMergeNode[dest] -> {
frames[dest] = frame setFrame(dest, frame)
true true
} }
oldFrame == null -> { oldFrame == null -> {
frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) } setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) })
true true
} }
!isMergeNode[dest] -> { !isMergeNode[dest] -> {
@@ -232,10 +193,7 @@ open class FastMethodAnalyzer<V : Value>
throw AnalyzerException(null, "${e.message}\nframe: ${frame.dump()}\noldFrame: ${oldFrame.dump()}") throw AnalyzerException(null, "${e.message}\nframe: ${frame.dump()}\noldFrame: ${oldFrame.dump()}")
} }
} }
if (changes && !queued[dest]) { updateQueue(changes, dest)
queued[dest] = true
queue[top++] = dest
}
} }
companion object { companion object {
@@ -33,12 +33,10 @@
package org.jetbrains.kotlin.codegen.optimization.fixStack package org.jetbrains.kotlin.codegen.optimization.fixStack
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
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.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
@@ -51,20 +49,14 @@ internal open class FastStackAnalyzer<V : Value>(
method: MethodNode, method: MethodNode,
interpreter: Interpreter<V> interpreter: Interpreter<V>
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) { ) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns) override fun newFrame(nLocals: Int, nStack: Int): Frame<V> = Frame(nLocals, nStack)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
private var top = 0
protected open fun newFrame(nLocals: Int, nStack: Int): Frame<V> = Frame(nLocals, nStack)
protected open fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean = true protected open fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean = true
protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true
fun analyze(): Array<Frame<V>?> { fun analyze(): Array<Frame<V>?> {
if (nInsns == 0) return frames if (nInsns == 0) return getFrames()
// This is a very specific version of method bytecode analyzer that doesn't perform any DFA, // This is a very specific version of method bytecode analyzer that doesn't perform any DFA,
// but infers stack types for reachable instructions instead. // but infers stack types for reachable instructions instead.
@@ -74,56 +66,36 @@ internal open class FastStackAnalyzer<V : Value>(
// Don't have to visit same exception handler multiple times - we care only about stack state at TCB start. // Don't have to visit same exception handler multiple times - we care only about stack state at TCB start.
computeExceptionHandlers(method, forEachInsn = false) computeExceptionHandlers(method, forEachInsn = false)
val current = newFrame(method.maxLocals, method.maxStack) analyzeInner()
val handler = newFrame(method.maxLocals, method.maxStack)
initControlFlowAnalysis(current, method, owner)
while (top > 0) { return getFrames()
val insn = queue[--top]
val f = frames[insn]!!
queued[insn] = false
val insnNode = method.instructions[insn]
val insnOpcode = insnNode.opcode
val insnType = insnNode.type
try {
analyzeInstruction(insnType, insnNode, f, insn, current, insnOpcode, handler)
} catch (e: AnalyzerException) {
throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
} catch (e: Exception) {
throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
}
}
return frames
} }
private fun analyzeInstruction( override fun analyzeInstruction(
insnType: Int,
insnNode: AbstractInsnNode, insnNode: AbstractInsnNode,
f: Frame<V>, insnIndex: Int,
insn: Int, insnType: Int,
current: Frame<V>,
insnOpcode: Int, insnOpcode: Int,
currentlyAnalyzing: Frame<V>,
current: Frame<V>,
handler: Frame<V>, handler: Frame<V>,
) { ) {
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
visitNopInsn(insnNode, f, insn) visitNopInsn(insnNode, currentlyAnalyzing, insnIndex)
} else { } else {
current.init(f) current.init(currentlyAnalyzing)
if (insnOpcode != Opcodes.RETURN) { if (insnOpcode != Opcodes.RETURN) {
// Don't care about possibly incompatible return type // Don't care about possibly incompatible return type
current.execute(insnNode, interpreter) current.execute(insnNode, interpreter)
} }
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex)
} }
handlers[insn]?.forEach { tcb -> handlers[insnIndex]?.forEach { tcb ->
val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable") val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
val jump = tcb.handler.indexOf() val jump = tcb.handler.indexOf()
if (visitControlFlowExceptionEdge(insn, tcb.handler.indexOf())) { if (visitControlFlowExceptionEdge(insnIndex, jump)) {
handler.init(f) handler.init(currentlyAnalyzing)
handler.clearStack() handler.clearStack()
handler.push(interpreter.newValue(exnType)) handler.push(interpreter.newValue(exnType))
mergeControlFlowEdge(jump, handler) mergeControlFlowEdge(jump, handler)
@@ -131,9 +103,6 @@ internal open class FastStackAnalyzer<V : Value>(
} }
} }
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
frames[insn.indexOf()]
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) { override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
processControlFlowEdge(current, insnNode, insn + 1) processControlFlowEdge(current, insnNode, insn + 1)
} }
@@ -179,11 +148,11 @@ internal open class FastStackAnalyzer<V : Value>(
} }
} }
private fun initControlFlowAnalysis(current: Frame<V>, m: MethodNode, owner: String) { override fun initLocals(current: Frame<V>) {
current.setReturn(interpreter.newValue(Type.getReturnType(m.desc))) current.setReturn(interpreter.newValue(Type.getReturnType(method.desc)))
val args = Type.getArgumentTypes(m.desc) val args = Type.getArgumentTypes(method.desc)
var local = 0 var local = 0
if ((m.access and Opcodes.ACC_STATIC) == 0) { if ((method.access and Opcodes.ACC_STATIC) == 0) {
val ctype = Type.getObjectType(owner) val ctype = Type.getObjectType(owner)
current.setLocal(local++, interpreter.newValue(ctype)) current.setLocal(local++, interpreter.newValue(ctype))
} }
@@ -193,21 +162,17 @@ internal open class FastStackAnalyzer<V : Value>(
current.setLocal(local++, interpreter.newValue(null)) current.setLocal(local++, interpreter.newValue(null))
} }
} }
while (local < m.maxLocals) { while (local < method.maxLocals) {
current.setLocal(local++, interpreter.newValue(null)) current.setLocal(local++, interpreter.newValue(null))
} }
mergeControlFlowEdge(0, current)
} }
private fun mergeControlFlowEdge(dest: Int, frame: Frame<V>) { override fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean) {
val destFrame = frames[dest] val destFrame = getFrame(dest)
if (destFrame == null) { if (destFrame == null) {
// Don't have to visit same instruction multiple times - we care only about "initial" stack state. // Don't have to visit same instruction multiple times - we care only about "initial" stack state.
frames[dest] = newFrame(frame.locals, frame.maxStackSize).apply { init(frame) } setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) })
if (!queued[dest]) { updateQueue(true, dest)
queued[dest] = true
queue[top++] = dest
}
} }
} }
@@ -33,13 +33,11 @@
package org.jetbrains.kotlin.codegen.optimization.temporaryVals package org.jetbrains.kotlin.codegen.optimization.temporaryVals
import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION
import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
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.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
@@ -107,70 +105,43 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
interpreter: StoreLoadInterpreter<V> interpreter: StoreLoadInterpreter<V>
) : FastAnalyzer<V, StoreLoadInterpreter<V>, StoreLoadFrame<V>>(owner, method, interpreter) { ) : FastAnalyzer<V, StoreLoadInterpreter<V>, StoreLoadFrame<V>>(owner, method, interpreter) {
private val isMergeNode = BooleanArray(nInsns) private val isMergeNode = BooleanArray(nInsns)
private val frames: Array<StoreLoadFrame<V>?> = arrayOfNulls(nInsns)
private val queued = BooleanArray(nInsns) fun analyze(): Array<Frame<V>?> {
private val queue = IntArray(nInsns) if (nInsns == 0) return getFrames()
private var top = 0
fun analyze(): Array<StoreLoadFrame<V>?> {
if (nInsns == 0) return frames
checkAssertions() checkAssertions()
computeExceptionHandlers(method) computeExceptionHandlers(method)
initMergeNodes() initMergeNodes()
val current = newFrame(method.maxLocals) analyzeInner()
val handler = newFrame(method.maxLocals)
initLocals(current)
mergeControlFlowEdge(0, current)
while (top > 0) { return getFrames()
val insn = queue[--top]
val f = frames[insn]!!
queued[insn] = false
val insnNode = method.instructions[insn]
val insnOpcode = insnNode.opcode
val insnType = insnNode.type
try {
analyzeInstruction(insnType, insn, f, current, insnNode, insnOpcode, handler)
} catch (e: AnalyzerException) {
throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}", e)
} catch (e: Exception) {
throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText(method.instructions)}: ${e.message}", e)
}
}
return frames
} }
private fun analyzeInstruction( override fun analyzeInstruction(
insnType: Int,
insn: Int,
f: StoreLoadFrame<V>,
current: StoreLoadFrame<V>,
insnNode: AbstractInsnNode, insnNode: AbstractInsnNode,
insnIndex: Int,
insnType: Int,
insnOpcode: Int, insnOpcode: Int,
currentlyAnalyzing: StoreLoadFrame<V>,
current: StoreLoadFrame<V>,
handler: StoreLoadFrame<V>, handler: StoreLoadFrame<V>,
) { ) {
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
mergeControlFlowEdge(insn + 1, f) mergeControlFlowEdge(insnIndex + 1, currentlyAnalyzing)
} else { } else {
current.init(f).execute(insnNode, interpreter) current.init(currentlyAnalyzing).execute(insnNode, interpreter)
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn) visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insnIndex)
} }
handlers[insn]?.forEach { tcb -> handlers[insnIndex]?.forEach { tcb ->
val jump = tcb.handler.indexOf() val jump = tcb.handler.indexOf()
handler.init(f) handler.init(currentlyAnalyzing)
mergeControlFlowEdge(jump, handler) mergeControlFlowEdge(jump, handler)
} }
} }
private fun newFrame(maxLocals: Int) = override fun newFrame(nLocals: Int, nStack: Int): StoreLoadFrame<V> = StoreLoadFrame<V>(nLocals)
StoreLoadFrame<V>(maxLocals)
override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame<V>, insn: Int) { override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame<V>, insn: Int) {
mergeControlFlowEdge(insn + 1, current) mergeControlFlowEdge(insn + 1, current)
@@ -225,7 +196,7 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
} }
} }
private fun initLocals(current: StoreLoadFrame<V>) { override fun initLocals(current: StoreLoadFrame<V>) {
val args = Type.getArgumentTypes(method.desc) val args = Type.getArgumentTypes(method.desc)
var local = 0 var local = 0
if ((method.access and Opcodes.ACC_STATIC) == 0) { if ((method.access and Opcodes.ACC_STATIC) == 0) {
@@ -247,11 +218,11 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
} }
} }
private fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame<V>) { override fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame<V>, canReuse: Boolean) {
val oldFrame = frames[dest] val oldFrame = getFrame(dest)
val changes = when { val changes = when {
oldFrame == null -> { oldFrame == null -> {
frames[dest] = newFrame(frame.maxLocals).init(frame) setFrame(dest, newFrame(frame.maxLocals, 0).init(frame))
true true
} }
!isMergeNode[dest] -> { !isMergeNode[dest] -> {
@@ -261,10 +232,6 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
else -> else ->
oldFrame.merge(frame, interpreter) oldFrame.merge(frame, interpreter)
} }
if (changes && !queued[dest]) { updateQueue(changes, dest)
queued[dest] = true
queue[top++] = dest
}
} }
} }
@@ -106,7 +106,7 @@ class TemporaryValsAnalyzer {
// Exclude stores observed at LVT liveness range start using information from bytecode analysis. // Exclude stores observed at LVT liveness range start using information from bytecode analysis.
for (lv in methodNode.localVariables) { for (lv in methodNode.localVariables) {
val frameAtStart = frames[insnList.indexOf(lv.start)] ?: continue val frameAtStart = frames[insnList.indexOf(lv.start)] ?: continue
when (val valueAtStart = frameAtStart[lv.index]) { when (val valueAtStart = frameAtStart.getLocal(lv.index)) {
is StoredValue.Store -> is StoredValue.Store ->
valueAtStart.temporaryVal.isDirty = true valueAtStart.temporaryVal.isDirty = true
is StoredValue.DirtyStore -> is StoredValue.DirtyStore ->