[JVM] Extract common code from Fast...Analyzer into analyzeInner
This commit is contained in:
+79
-1
@@ -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")
|
||||||
|
|||||||
+21
-63
@@ -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 {
|
||||||
|
|||||||
+24
-59
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+20
-53
@@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+1
-1
@@ -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 ->
|
||||||
|
|||||||
Reference in New Issue
Block a user