[JVM] Create base FastAnalyzer class for all Fast...Analyzer

This commit is contained in:
Ivan Kylchik
2023-08-23 19:16:51 +02:00
committed by Space Team
parent 7853256c02
commit 98cc2c839f
6 changed files with 167 additions and 144 deletions
@@ -0,0 +1,73 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
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
abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F: Frame<V>>(
protected val owner: String,
protected val method: MethodNode,
protected val interpreter: I,
) {
protected val nInsns = method.instructions.size()
protected val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
protected fun visitMeaningfulInstruction(insnNode: AbstractInsnNode, insnType: Int, insnOpcode: Int, current: F, insn: Int) {
when {
insnType == AbstractInsnNode.JUMP_INSN ->
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
visitOpInsn(insnNode, current, insn)
else -> {
}
}
}
protected abstract fun visitJumpInsnNode(insnNode: JumpInsnNode, current: F, insn: Int, insnOpcode: Int)
protected abstract fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: F)
protected abstract fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: F)
protected abstract fun visitOpInsn(insnNode: AbstractInsnNode, current: F, insn: Int)
protected fun checkAssertions() {
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
throw AssertionError("Subroutines are deprecated since Java 6")
}
protected fun AbstractInsnNode.indexOf() =
method.instructions.indexOf(this)
protected fun F.dump(): String {
return buildString {
append("{\n")
append(" locals: [\n")
for (i in 0 until method.maxLocals) {
append(" #$i: ${this@dump.getLocal(i)}\n")
}
append(" ]\n")
val stackSize = this@dump.stackSize
append(" stack: size=")
append(stackSize)
if (stackSize == 0) {
append(" []\n")
} else {
append(" [\n")
for (i in 0 until stackSize) {
append(" #$i: ${this@dump.getStack(i)}\n")
}
append(" ]\n")
}
append("}\n")
}
}
}
@@ -46,21 +46,16 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
/**
* @see org.jetbrains.kotlin.codegen.optimization.fixStack.FastStackAnalyzer
*/
@Suppress("DuplicatedCode")
open class FastMethodAnalyzer<V : Value>
@JvmOverloads constructor(
private val owner: String,
private val method: MethodNode,
private val interpreter: Interpreter<V>,
owner: String,
method: MethodNode,
interpreter: Interpreter<V>,
private val pruneExceptionEdges: Boolean = false
) {
private val nInsns = method.instructions.size()
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
private val isMergeNode = findMergeNodes(method)
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
private var top = 0
@@ -102,18 +97,7 @@ open class FastMethodAnalyzer<V : Value>
mergeControlFlowEdge(insn + 1, f, canReuse = true)
} else {
current.init(f).execute(insnNode, interpreter)
when {
insnType == AbstractInsnNode.JUMP_INSN ->
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
visitOpInsn(current, insn)
else -> {
}
}
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
}
// Jump by an exception edge clears the stack, putting exception on top.
@@ -155,7 +139,7 @@ open class FastMethodAnalyzer<V : Value>
return frames
}
internal fun initLocals(current: Frame<V>) {
private fun initLocals(current: Frame<V>) {
current.setReturn(interpreter.newReturnTypeValue(Type.getReturnType(method.desc)))
val args = Type.getArgumentTypes(method.desc)
var local = 0
@@ -178,22 +162,14 @@ open class FastMethodAnalyzer<V : Value>
}
}
private fun AbstractInsnNode.indexOf() =
method.instructions.indexOf(this)
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
frames[insn.indexOf()]
private fun checkAssertions() {
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
throw AssertionError("Subroutines are deprecated since Java 6")
}
private fun visitOpInsn(current: Frame<V>, insn: Int) {
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
mergeControlFlowEdge(insn + 1, current)
}
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
// 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
@@ -205,14 +181,14 @@ open class FastMethodAnalyzer<V : Value>
}
}
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
for (label in insnNode.labels) {
mergeControlFlowEdge(label.indexOf(), current)
}
}
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
mergeControlFlowEdge(insnNode.label.indexOf(), current)
if (insnOpcode != Opcodes.GOTO) {
mergeControlFlowEdge(insn + 1, current)
@@ -272,30 +248,6 @@ open class FastMethodAnalyzer<V : Value>
}
}
private fun Frame<V>.dump(): String {
return buildString {
append("{\n")
append(" locals: [\n")
for (i in 0 until method.maxLocals) {
append(" #$i: ${this@dump.getLocal(i)}\n")
}
append(" ]\n")
val stackSize = this@dump.stackSize
append(" stack: size=")
append(stackSize)
if (stackSize == 0) {
append(" []\n")
} else {
append(" [\n")
for (i in 0 until stackSize) {
append(" #$i: ${this@dump.getStack(i)}\n")
}
append(" ]\n")
}
append("}\n")
}
}
companion object {
fun findMergeNodes(method: MethodNode): BooleanArray {
val isMergeNode = BooleanArray(method.instructions.size())
@@ -34,6 +34,7 @@
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.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.tree.*
@@ -45,17 +46,13 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Value
/**
* @see org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
*/
@Suppress("DuplicatedCode")
internal open class FastStackAnalyzer<V : Value>(
private val owner: String,
val method: MethodNode,
protected val interpreter: Interpreter<V>
) {
private val nInsns = method.instructions.size()
owner: String,
method: MethodNode,
interpreter: Interpreter<V>
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter) {
private val frames: Array<Frame<V>?> = arrayOfNulls(nInsns)
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
private var top = 0
@@ -98,19 +95,7 @@ internal open class FastStackAnalyzer<V : Value>(
// Don't care about possibly incompatible return type
current.execute(insnNode, interpreter)
}
when {
insnType == AbstractInsnNode.JUMP_INSN ->
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
visitOpInsn(insnNode, current, insn)
else -> {
}
}
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
}
handlers[insn]?.forEach { tcb ->
@@ -135,17 +120,10 @@ internal open class FastStackAnalyzer<V : Value>(
return frames
}
private fun AbstractInsnNode.indexOf() = method.instructions.indexOf(this)
fun getFrame(insn: AbstractInsnNode): Frame<V>? =
frames[insn.indexOf()]
private fun checkAssertions() {
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
throw AssertionError("Subroutines are deprecated since Java 6")
}
private fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
override fun visitOpInsn(insnNode: AbstractInsnNode, current: Frame<V>, insn: Int) {
processControlFlowEdge(current, insnNode, insn + 1)
}
@@ -153,7 +131,7 @@ internal open class FastStackAnalyzer<V : Value>(
processControlFlowEdge(f, insnNode, insn + 1)
}
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: Frame<V>) {
var jump = insnNode.dflt.indexOf()
processControlFlowEdge(current, insnNode, jump)
// In most cases order of visiting switch labels should not matter
@@ -167,7 +145,7 @@ internal open class FastStackAnalyzer<V : Value>(
}
}
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: Frame<V>) {
var jump = insnNode.dflt.indexOf()
processControlFlowEdge(current, insnNode, jump)
for (label in insnNode.labels) {
@@ -176,7 +154,7 @@ internal open class FastStackAnalyzer<V : Value>(
}
}
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: Frame<V>, insn: Int, insnOpcode: Int) {
if (insnOpcode != Opcodes.GOTO) {
processControlFlowEdge(current, insnNode, insn + 1)
}
@@ -103,8 +103,6 @@ internal class FixStackAnalyzer(
override fun newFrame(nLocals: Int, nStack: Int): Frame<FixStackValue> =
FixStackFrame(nLocals, nStack)
private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node)
inner class FixStackFrame(nLocals: Int, nStack: Int) : Frame<FixStackValue>(nLocals, nStack) {
val extraStack = Stack<FixStackValue>()
@@ -207,7 +205,7 @@ internal class FixStackAnalyzer(
private fun FixStackFrame.executeRestoreStackInTryCatch(insn: AbstractInsnNode) {
val saveNode = context.saveStackMarkerForRestoreMarker[insn]
val savedValues = spilledStacks.getOrElse(saveNode!!) {
throw AssertionError("${indexOf(insn)}: Restore stack is unavailable for ${indexOf(saveNode)}")
throw AssertionError("${insn.indexOf()}: Restore stack is unavailable for ${saveNode.indexOf()}")
}
pushAll(savedValues)
}
@@ -34,28 +34,30 @@
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.isMeaningful
import org.jetbrains.kotlin.utils.SmartList
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Opcodes.API_VERSION
import org.jetbrains.org.objectweb.asm.Type
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.Interpreter
import org.jetbrains.org.objectweb.asm.tree.analysis.Value
interface StoreLoadValue
interface StoreLoadValue : Value
interface StoreLoadInterpreter<V : StoreLoadValue> {
fun uninitialized(): V
fun valueParameter(type: Type): V
fun store(insn: VarInsnNode): V
fun load(insn: VarInsnNode, value: V)
fun iinc(insn: IincInsnNode, value: V): V
fun merge(a: V, b: V): V
abstract class StoreLoadInterpreter<V : StoreLoadValue> : Interpreter<V>(API_VERSION) {
abstract fun uninitialized(): V
abstract fun valueParameter(type: Type): V
abstract fun store(insn: VarInsnNode): V
abstract fun load(insn: VarInsnNode, value: V)
abstract fun iinc(insn: IincInsnNode, value: V): V
}
@Suppress("UNCHECKED_CAST")
class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) {
class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) : Frame<V>(maxLocals, 0) {
private val locals = arrayOfNulls<StoreLoadValue>(maxLocals)
operator fun get(index: Int): V =
@@ -101,19 +103,14 @@ class StoreLoadFrame<V : StoreLoadValue>(val maxLocals: Int) {
}
}
@Suppress("DuplicatedCode")
class FastStoreLoadAnalyzer<V : StoreLoadValue>(
private val owner: String,
private val method: MethodNode,
private val interpreter: StoreLoadInterpreter<V>
) {
private val nInsns = method.instructions.size()
owner: String,
method: MethodNode,
interpreter: StoreLoadInterpreter<V>
) : FastAnalyzer<V, StoreLoadInterpreter<V>, StoreLoadFrame<V>>(owner, method, interpreter) {
private val isMergeNode = BooleanArray(nInsns)
private val frames: Array<StoreLoadFrame<V>?> = arrayOfNulls(nInsns)
private val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
private var top = 0
@@ -144,18 +141,7 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
mergeControlFlowEdge(insn + 1, f)
} else {
current.init(f).execute(insnNode, interpreter)
when {
insnType == AbstractInsnNode.JUMP_INSN ->
visitJumpInsnNode(insnNode as JumpInsnNode, current, insn, insnOpcode)
insnType == AbstractInsnNode.LOOKUPSWITCH_INSN ->
visitLookupSwitchInsnNode(insnNode as LookupSwitchInsnNode, current)
insnType == AbstractInsnNode.TABLESWITCH_INSN ->
visitTableSwitchInsnNode(insnNode as TableSwitchInsnNode, current)
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
mergeControlFlowEdge(insn + 1, current)
else -> {
}
}
visitMeaningfulInstruction(insnNode, insnType, insnOpcode, current, insn)
}
handlers[insn]?.forEach { tcb ->
@@ -177,29 +163,25 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
private fun newFrame(maxLocals: Int) =
StoreLoadFrame<V>(maxLocals)
private fun AbstractInsnNode.indexOf() =
method.instructions.indexOf(this)
private fun checkAssertions() {
if (method.instructions.any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
throw AssertionError("Subroutines are deprecated since Java 6")
override fun visitOpInsn(insnNode: AbstractInsnNode, current: StoreLoadFrame<V>, insn: Int) {
mergeControlFlowEdge(insn + 1, current)
}
private fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame<V>) {
override fun visitTableSwitchInsnNode(insnNode: TableSwitchInsnNode, current: StoreLoadFrame<V>) {
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
for (label in insnNode.labels) {
mergeControlFlowEdge(label.indexOf(), current)
}
}
private fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame<V>) {
override fun visitLookupSwitchInsnNode(insnNode: LookupSwitchInsnNode, current: StoreLoadFrame<V>) {
mergeControlFlowEdge(insnNode.dflt.indexOf(), current)
for (label in insnNode.labels) {
mergeControlFlowEdge(label.indexOf(), current)
}
}
private fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame<V>, insn: Int, insnOpcode: Int) {
override fun visitJumpInsnNode(insnNode: JumpInsnNode, current: StoreLoadFrame<V>, insn: Int, insnOpcode: Int) {
if (insnOpcode != Opcodes.GOTO) {
mergeControlFlowEdge(insn + 1, current)
}
@@ -254,21 +236,25 @@ class FastStoreLoadAnalyzer<V : StoreLoadValue>(
}
}
internal fun initLocals(current: StoreLoadFrame<V>) {
private fun initLocals(current: StoreLoadFrame<V>) {
val args = Type.getArgumentTypes(method.desc)
var local = 0
if ((method.access and Opcodes.ACC_STATIC) == 0) {
val ctype = Type.getObjectType(owner)
current[local++] = interpreter.valueParameter(ctype)
current.setLocal(local, interpreter.newValue(ctype))
local++
}
for (arg in args) {
current[local++] = interpreter.valueParameter(arg)
current.setLocal(local, interpreter.newValue(arg))
local++
if (arg.size == 2) {
current[local++] = interpreter.uninitialized()
current.setLocal(local, interpreter.uninitialized())
local++
}
}
while (local < method.maxLocals) {
current[local++] = interpreter.uninitialized()
current.setLocal(local, interpreter.uninitialized())
local++
}
}
@@ -134,6 +134,9 @@ class TemporaryValsAnalyzer {
}
private sealed class StoredValue : StoreLoadValue {
// `StoredValue` represent some abstract value that doesn't really have a definition of size.
// `getSize` can return either 1 or 2, so we use 1 here.
override fun getSize(): Int = 1
object Unknown : StoredValue()
@@ -156,13 +159,13 @@ class TemporaryValsAnalyzer {
private class StoreTrackingInterpreter(
private val storeInsnToStoreData: Map<AbstractInsnNode, StoreData>
) : StoreLoadInterpreter<StoredValue> {
) : StoreLoadInterpreter<StoredValue>() {
override fun uninitialized(): StoredValue =
StoredValue.Unknown
override fun uninitialized(): StoredValue = StoredValue.Unknown
override fun valueParameter(type: Type): StoredValue =
StoredValue.Unknown
override fun newValue(type: Type?): StoredValue = StoredValue.Unknown
override fun valueParameter(type: Type): StoredValue = StoredValue.Unknown
override fun store(insn: VarInsnNode): StoredValue {
val temporaryValData = storeInsnToStoreData[insn]
@@ -218,5 +221,38 @@ class TemporaryValsAnalyzer {
is StoredValue.DirtyStore -> this.temporaryVals
else -> emptySet()
}
override fun copyOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue {
TODO("Not yet implemented")
}
override fun newOperation(insn: AbstractInsnNode?): StoredValue {
TODO("Not yet implemented")
}
override fun unaryOperation(insn: AbstractInsnNode?, value: StoredValue?): StoredValue {
TODO("Not yet implemented")
}
override fun binaryOperation(insn: AbstractInsnNode?, value1: StoredValue?, value2: StoredValue?): StoredValue {
TODO("Not yet implemented")
}
override fun ternaryOperation(
insn: AbstractInsnNode?,
value1: StoredValue?,
value2: StoredValue?,
value3: StoredValue?,
): StoredValue {
TODO("Not yet implemented")
}
override fun naryOperation(insn: AbstractInsnNode?, values: MutableList<out StoredValue>?): StoredValue {
TODO("Not yet implemented")
}
override fun returnOperation(insn: AbstractInsnNode?, value: StoredValue?, expected: StoredValue?) {
TODO("Not yet implemented")
}
}
}