[JVM] Combine all mergeControlFlowEdge methods

This is just a refactoring commit. All fast analyzers are doing the
same thing in `mergeControlFlowEdge`, so we extract it in common
`FastAnalyzer`. The only exception is `FastStackAnalyzer`.
There was no `isMergeNode` check, but this is an optimization
that allows us to reuse a frame and not copy it.
This commit is contained in:
Ivan Kylchik
2023-09-06 11:39:12 +02:00
committed by Space Team
parent 68e4a7a318
commit 53d9b3c54f
5 changed files with 75 additions and 106 deletions
@@ -16,11 +16,14 @@
package org.jetbrains.kotlin.codegen.optimization.boxing
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.findPreviousOrNull
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.InsnNode
import org.jetbrains.org.objectweb.asm.tree.LdcInsnNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class StackPeepholeOptimizationsTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) {
@@ -33,7 +36,7 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
val instructions = methodNode.instructions
var changed = false
val isMergeNode = FastMethodAnalyzer.findMergeNodes(methodNode)
val isMergeNode = FastAnalyzer.findMergeNodes(methodNode)
fun AbstractInsnNode.previousMeaningful() =
findPreviousOrNull {
@@ -16,9 +16,9 @@ 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,
private val owner: String,
protected val method: MethodNode,
protected val interpreter: I,
private val interpreter: I,
private val pruneExceptionEdges: Boolean,
) {
private val nInsns = method.instructions.size()
@@ -26,6 +26,7 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F : Frame<V>>(
protected val handlers: Array<MutableList<TryCatchBlockNode>?> = arrayOfNulls(nInsns)
protected val isTcbStart = BooleanArray(nInsns)
private val isMergeNode = findMergeNodes(method)
private val queued = BooleanArray(nInsns)
private val queue = IntArray(nInsns)
@@ -109,7 +110,38 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F : Frame<V>>(
}
}
protected abstract fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean = false)
protected open fun useFastMergeControlFlowEdge(): Boolean = false
/**
* Updates frame at the index [dest] with its old value if provided and previous control flow node frame [frame].
* Reuses old frame when possible and when [canReuse] is true.
* If updated, adds the frame to the queue
*/
private fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean = false) {
val oldFrame = getFrame(dest)
val changes = when {
canReuse && !isMergeNode[dest] -> {
setFrame(dest, frame)
true
}
oldFrame == null -> {
setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) })
true
}
!isMergeNode[dest] -> {
oldFrame.init(frame)
true
}
!useFastMergeControlFlowEdge() ->
try {
oldFrame.merge(frame, interpreter)
} catch (e: AnalyzerException) {
throw AnalyzerException(null, "${e.message}\nframe: ${frame.dump()}\noldFrame: ${oldFrame.dump()}")
}
else -> false
}
updateQueue(changes, dest)
}
private fun analyzeInstruction(
insnNode: AbstractInsnNode,
@@ -294,4 +326,36 @@ abstract class FastAnalyzer<V : Value, I : Interpreter<V>, F : Frame<V>>(
append("}\n")
}
}
companion object {
fun findMergeNodes(method: MethodNode): BooleanArray {
val isMergeNode = BooleanArray(method.instructions.size())
for (insn in method.instructions) {
when (insn.type) {
AbstractInsnNode.JUMP_INSN -> {
val jumpInsn = insn as JumpInsnNode
isMergeNode[method.instructions.indexOf(jumpInsn.label)] = true
}
AbstractInsnNode.LOOKUPSWITCH_INSN -> {
val switchInsn = insn as LookupSwitchInsnNode
isMergeNode[method.instructions.indexOf(switchInsn.dflt)] = true
for (label in switchInsn.labels) {
isMergeNode[method.instructions.indexOf(label)] = true
}
}
AbstractInsnNode.TABLESWITCH_INSN -> {
val switchInsn = insn as TableSwitchInsnNode
isMergeNode[method.instructions.indexOf(switchInsn.dflt)] = true
for (label in switchInsn.labels) {
isMergeNode[method.instructions.indexOf(label)] = true
}
}
}
}
for (tcb in method.tryCatchBlocks) {
isMergeNode[method.instructions.indexOf(tcb.handler)] = true
}
return isMergeNode
}
}
}
@@ -33,8 +33,7 @@
package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.AnalyzerException
import org.jetbrains.org.objectweb.asm.tree.MethodNode
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
@@ -50,8 +49,6 @@ class FastMethodAnalyzer<V : Value>
pruneExceptionEdges: Boolean = false,
private val createFrame: (Int, Int) -> Frame<V> = { nLocals, nStack -> Frame<V>(nLocals, nStack) }
) : FastAnalyzer<V, Interpreter<V>, Frame<V>>(owner, method, interpreter, pruneExceptionEdges) {
private val isMergeNode = findMergeNodes(method)
override fun newFrame(nLocals: Int, nStack: Int): Frame<V> = createFrame(nLocals, nStack)
override fun beforeAnalyze() {
@@ -59,66 +56,4 @@ class FastMethodAnalyzer<V : Value>
isTcbStart[tcb.start.indexOf() + 1] = true
}
}
/**
* Updates frame at the index [dest] with its old value if provided and previous control flow node frame [frame].
* Reuses old frame when possible and when [canReuse] is true.
* If updated, adds the frame to the queue
*/
override fun mergeControlFlowEdge(dest: Int, frame: Frame<V>, canReuse: Boolean) {
val oldFrame = getFrame(dest)
val changes = when {
canReuse && !isMergeNode[dest] -> {
setFrame(dest, frame)
true
}
oldFrame == null -> {
setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) })
true
}
!isMergeNode[dest] -> {
oldFrame.init(frame)
true
}
else ->
try {
oldFrame.merge(frame, interpreter)
} catch (e: AnalyzerException) {
throw AnalyzerException(null, "${e.message}\nframe: ${frame.dump()}\noldFrame: ${oldFrame.dump()}")
}
}
updateQueue(changes, dest)
}
companion object {
fun findMergeNodes(method: MethodNode): BooleanArray {
val isMergeNode = BooleanArray(method.instructions.size())
for (insn in method.instructions) {
when (insn.type) {
AbstractInsnNode.JUMP_INSN -> {
val jumpInsn = insn as JumpInsnNode
isMergeNode[method.instructions.indexOf(jumpInsn.label)] = true
}
AbstractInsnNode.LOOKUPSWITCH_INSN -> {
val switchInsn = insn as LookupSwitchInsnNode
isMergeNode[method.instructions.indexOf(switchInsn.dflt)] = true
for (label in switchInsn.labels) {
isMergeNode[method.instructions.indexOf(label)] = true
}
}
AbstractInsnNode.TABLESWITCH_INSN -> {
val switchInsn = insn as TableSwitchInsnNode
isMergeNode[method.instructions.indexOf(switchInsn.dflt)] = true
for (label in switchInsn.labels) {
isMergeNode[method.instructions.indexOf(label)] = true
}
}
}
}
for (tcb in method.tryCatchBlocks) {
isMergeNode[method.instructions.indexOf(tcb.handler)] = true
}
return isMergeNode
}
}
}
@@ -54,18 +54,5 @@ internal open class FastStackAnalyzer<V : Value, F : Frame<V>>(
// Don't have to visit the same exception handler multiple times - we care only about stack state at TCB start.
override fun useFastComputeExceptionHandlers(): Boolean = true
override fun mergeControlFlowEdge(dest: Int, frame: F, canReuse: Boolean) {
val oldFrame = getFrame(dest)
val changes = when {
// Don't have to visit the same instruction multiple times - we care only about "initial" stack state.
oldFrame == null -> {
setFrame(dest, newFrame(frame.locals, frame.maxStackSize).apply { init(frame) })
true
}
else -> false
}
updateQueue(changes, dest)
}
override fun useFastMergeControlFlowEdge(): Boolean = true
}
@@ -34,7 +34,6 @@
package org.jetbrains.kotlin.codegen.optimization.temporaryVals
import org.jetbrains.kotlin.codegen.optimization.common.FastAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.FastMethodAnalyzer
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.IincInsnNode
@@ -68,24 +67,5 @@ class FastStoreLoadAnalyzer<V : Value>(
method: MethodNode,
interpreter: Interpreter<V>
) : FastAnalyzer<V, Interpreter<V>, StoreLoadFrame<V>>(owner, method, interpreter, pruneExceptionEdges = false) {
private val isMergeNode = FastMethodAnalyzer.findMergeNodes(method)
override fun newFrame(nLocals: Int, nStack: Int): StoreLoadFrame<V> = StoreLoadFrame(nLocals)
override fun mergeControlFlowEdge(dest: Int, frame: StoreLoadFrame<V>, canReuse: Boolean) {
val oldFrame = getFrame(dest)
val changes = when {
oldFrame == null -> {
setFrame(dest, newFrame(frame.maxLocals, 0).apply { init(frame) })
true
}
!isMergeNode[dest] -> {
oldFrame.init(frame)
true
}
else ->
oldFrame.merge(frame, interpreter)
}
updateQueue(changes, dest)
}
}