[JVM] Make FixStackAnalyzer inherit FastStackAnalyzer

This commit is contained in:
Ivan Kylchik
2023-08-24 12:50:54 +02:00
committed by Space Team
parent f49f6656b9
commit 5f27e1a6b2
3 changed files with 136 additions and 148 deletions
@@ -116,7 +116,7 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F : Frame<V>>(
protected abstract fun newFrame(nLocals: Int, nStack: Int): F
@Suppress("UNCHECKED_CAST")
fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F
protected fun getFrame(insn: AbstractInsnNode): F? = frames[insn.indexOf()] as? F
@Suppress("UNCHECKED_CAST")
protected fun getFrame(index: Int): F? = frames[index] as? F
@@ -46,12 +46,13 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
*/
// This is a very specific version of method bytecode analyzer that doesn't perform any DFA,
// but infers stack types for reachable instructions instead.
internal open class FastStackAnalyzer<V : Value>(
internal open class FastStackAnalyzer<V : Value, F : Frame<V>>(
owner: String,
method: MethodNode,
interpreter: Interpreter<V>
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
override fun newFrame(nLocals: Int, nStack: Int): Frame<V> = Frame(nLocals, nStack)
) : FastAnalyzer<V, Interpreter<V>, F>(owner, method, interpreter) {
@Suppress("UNCHECKED_CAST")
override fun newFrame(nLocals: Int, nStack: Int): F = Frame<V>(nLocals, nStack) as F
protected open fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean = true
@@ -65,9 +66,9 @@ internal open class FastStackAnalyzer<V : Value>(
insnIndex: Int,
insnType: Int,
insnOpcode: Int,
currentlyAnalyzing: Frame<V>,
current: Frame<V>,
handler: Frame<V>,
currentlyAnalyzing: F,
current: F,
handler: F,
) {
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
visitNopInsn(insnNode, currentlyAnalyzing, insnIndex)
@@ -92,15 +93,15 @@ internal open class FastStackAnalyzer<V : Value>(
}
}
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
override fun visitOpInsn(insnNode: AbstractInsnNode, current: F, insn: Int) {
processControlFlowEdge(current, insnNode, insn + 1)
}
private fun visitNopInsn(insnNode: AbstractInsnNode, f: Frame<V>, insn: Int) {
private fun visitNopInsn(insnNode: AbstractInsnNode, f: F, insn: Int) {
processControlFlowEdge(f, insnNode, insn + 1)
}
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: F) {
var jump = insnNode.dflt.indexOf()
processControlFlowEdge(current, insnNode, jump)
// In most cases order of visiting switch labels should not matter
@@ -114,7 +115,7 @@ internal open class FastStackAnalyzer<V : Value>(
}
}
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: F) {
var jump = insnNode.dflt.indexOf()
processControlFlowEdge(current, insnNode, jump)
for (label in insnNode.labels) {
@@ -123,7 +124,7 @@ internal open class FastStackAnalyzer<V : Value>(
}
}
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: F, insn: Int, insnOpcode: Int) {
if (insnOpcode != Opcodes.GOTO) {
processControlFlowEdge(current, insnNode, insn + 1)
}
@@ -131,13 +132,13 @@ internal open class FastStackAnalyzer<V : Value>(
processControlFlowEdge(current, insnNode, jump)
}
private fun processControlFlowEdge(current: Frame<V>, insnNode: AbstractInsnNode, jump: Int) {
private fun processControlFlowEdge(current: F, insnNode: AbstractInsnNode, jump: Int) {
if (visitControlFlowEdge(insnNode, jump)) {
mergeControlFlowEdge(jump, current)
}
}
override fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean) {
override fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean) {
val destFrame = getFrame(dest)
if (destFrame == null) {
// Don't have to visit same instruction multiple times - we care only about "initial" stack state.
@@ -32,10 +32,10 @@ import kotlin.math.max
internal class FixStackAnalyzer(
owner: String,
val method: MethodNode,
method: MethodNode,
val context: FixStackContext,
private val skipBreakContinueGotoEdges: Boolean = true
) {
) : FastStackAnalyzer<FixStackValue, FixStackAnalyzer.FixStackFrame>(owner, method, FixStackInterpreter()) {
companion object {
// Stack size is always non-negative
const val DEAD_CODE_STACK_SIZE = -1
@@ -43,10 +43,11 @@ internal class FixStackAnalyzer(
private val loopEntryPointMarkers = hashMapOf<LabelNode, SmartList<AbstractInsnNode>>()
val maxExtraStackSize: Int get() = analyzer.maxExtraStackSize
var maxExtraStackSize = 0; private set
private val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>()
fun getStackToSpill(location: AbstractInsnNode): List<FixStackValue>? =
analyzer.spilledStacks[location]
spilledStacks[location]
fun getActualStack(location: AbstractInsnNode): List<FixStackValue>? =
getFrame(location)?.getStackContent()
@@ -70,11 +71,8 @@ internal class FixStackAnalyzer(
return DEAD_CODE_STACK_SIZE
}
private fun getFrame(location: AbstractInsnNode) = analyzer.getFrame(location) as? InternalAnalyzer.FixStackFrame
fun analyze() {
override fun beforeAnalyze() {
recordLoopEntryPointMarkers()
analyzer.analyze()
}
private fun recordLoopEntryPointMarkers() {
@@ -87,133 +85,122 @@ internal class FixStackAnalyzer(
}
}
private val analyzer = InternalAnalyzer(owner)
private inner class InternalAnalyzer(owner: String) :
FastStackAnalyzer<FixStackValue>(owner, method, FixStackInterpreter()) {
val spilledStacks = hashMapOf<AbstractInsnNode, List<FixStackValue>>()
var maxExtraStackSize = 0; private set
override fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean {
if (!skipBreakContinueGotoEdges) return true
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
}
override fun newFrame(nLocals: Int, nStack: Int): Frame<FixStackValue> =
FixStackFrame(nLocals, nStack)
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
val extraStack = Stack<FixStackValue>()
override fun init(src: Frame<out FixStackValue>): Frame<FixStackValue> {
extraStack.clear()
extraStack.addAll((src as FixStackFrame).extraStack)
return super.init(src)
}
override fun clearStack() {
extraStack.clear()
super.clearStack()
}
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<FixStackValue>) {
when {
PseudoInsn.SAVE_STACK_BEFORE_TRY.isa(insn) ->
executeSaveStackBeforeTry(insn)
PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) ->
executeRestoreStackInTryCatch(insn)
isBeforeInlineMarker(insn) ->
executeBeforeInlineCallMarker(insn)
isAfterInlineMarker(insn) ->
executeAfterInlineCallMarker(insn)
insn.opcode == Opcodes.RETURN ->
return
}
super.execute(insn, interpreter)
}
val stackSizeWithExtra: Int get() = super.getStackSize() + extraStack.size
fun getStackContent(): List<FixStackValue> {
val savedStack = ArrayList<FixStackValue>()
for (i in 0 until super.getStackSize()) {
savedStack.add(super.getStack(i))
}
savedStack.addAll(extraStack)
return savedStack
}
override fun push(value: FixStackValue) {
if (super.getStackSize() < maxStackSize) {
super.push(value)
} else {
extraStack.add(value)
maxExtraStackSize = max(maxExtraStackSize, extraStack.size)
}
}
fun pushAll(values: Collection<FixStackValue>) {
values.forEach { push(it) }
}
override fun pop(): FixStackValue =
if (extraStack.isNotEmpty()) {
extraStack.pop()
} else {
super.pop()
}
override fun setStack(i: Int, value: FixStackValue) {
if (i < super.getMaxStackSize()) {
super.setStack(i, value)
} else {
extraStack[i - maxStackSize] = value
}
}
override fun merge(frame: Frame<out FixStackValue>, interpreter: Interpreter<FixStackValue>): Boolean {
throw UnsupportedOperationException("Stack normalization should not merge frames")
}
private fun executeBeforeInlineCallMarker(insn: AbstractInsnNode) {
saveStackAndClear(insn)
}
private fun saveStackAndClear(insn: AbstractInsnNode) {
val savedValues = getStackContent()
spilledStacks[insn] = savedValues
clearStack()
}
private fun executeAfterInlineCallMarker(insn: AbstractInsnNode) {
val beforeInlineMarker = context.openingInlineMethodMarker[insn]
if (stackSize > 0) {
val returnValue = pop()
clearStack()
val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!)
push(returnValue)
} else {
val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!)
}
}
private fun executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
val savedValues = spilledStacks.getOrElse(saveNode!!) {
throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}")
}
pushAll(savedValues)
}
private fun executeSaveStackBeforeTry(insn: AbstractInsnNode) {
saveStackAndClear(insn)
}
}
override fun visitControlFlowEdge(insnNode: AbstractInsnNode, successor: Int): Boolean {
if (!skipBreakContinueGotoEdges) return true
return !(insnNode is JumpInsnNode && context.breakContinueGotoNodes.contains(insnNode))
}
override fun newFrame(nLocals: Int, nStack: Int): FixStackFrame =
FixStackFrame(nLocals, nStack)
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
private val extraStack = Stack<FixStackValue>()
override fun init(src: Frame<out FixStackValue>): Frame<FixStackValue> {
extraStack.clear()
extraStack.addAll((src as FixStackFrame).extraStack)
return super.init(src)
}
override fun clearStack() {
extraStack.clear()
super.clearStack()
}
override fun execute(insn: AbstractInsnNode, interpreter: Interpreter<FixStackValue>) {
when {
PseudoInsn.SAVE_STACK_BEFORE_TRY.isa(insn) ->
executeSaveStackBeforeTry(insn)
PseudoInsn.RESTORE_STACK_IN_TRY_CATCH.isa(insn) ->
executeRestoreStackInTryCatch(insn)
isBeforeInlineMarker(insn) ->
executeBeforeInlineCallMarker(insn)
isAfterInlineMarker(insn) ->
executeAfterInlineCallMarker(insn)
insn.opcode == Opcodes.RETURN ->
return
}
super.execute(insn, interpreter)
}
val stackSizeWithExtra: Int get() = super.getStackSize() + extraStack.size
fun getStackContent(): List<FixStackValue> {
val savedStack = ArrayList<FixStackValue>()
for (i in 0 until super.getStackSize()) {
savedStack.add(super.getStack(i))
}
savedStack.addAll(extraStack)
return savedStack
}
override fun push(value: FixStackValue) {
if (super.getStackSize() < maxStackSize) {
super.push(value)
} else {
extraStack.add(value)
maxExtraStackSize = max(maxExtraStackSize, extraStack.size)
}
}
fun pushAll(values: Collection<FixStackValue>) {
values.forEach { push(it) }
}
override fun pop(): FixStackValue =
if (extraStack.isNotEmpty()) {
extraStack.pop()
} else {
super.pop()
}
override fun setStack(i: Int, value: FixStackValue) {
if (i < super.getMaxStackSize()) {
super.setStack(i, value)
} else {
extraStack[i - maxStackSize] = value
}
}
override fun merge(frame: Frame<out FixStackValue>, interpreter: Interpreter<FixStackValue>): Boolean {
throw UnsupportedOperationException("Stack normalization should not merge frames")
}
private fun executeBeforeInlineCallMarker(insn: AbstractInsnNode) {
saveStackAndClear(insn)
}
private fun saveStackAndClear(insn: AbstractInsnNode) {
val savedValues = getStackContent()
spilledStacks[insn] = savedValues
clearStack()
}
private fun executeAfterInlineCallMarker(insn: AbstractInsnNode) {
val beforeInlineMarker = context.openingInlineMethodMarker[insn]
if (stackSize > 0) {
val returnValue = pop()
clearStack()
val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!)
push(returnValue)
} else {
val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!)
}
}
private fun executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
val savedValues = spilledStacks.getOrElse(saveNode!!) {
throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}")
}
pushAll(savedValues)
}
private fun executeSaveStackBeforeTry(insn: AbstractInsnNode) {
saveStackAndClear(insn)
}
}
}