JVM: do not remove NOPs in PopBackwardPropagationTransformer

There is a pass that removes NOPs and runs afterwards anyway.
This commit is contained in:
pyos
2021-05-26 14:18:13 +02:00
committed by Alexander Udalov
parent 5f4be07225
commit 535934dc28
@@ -19,10 +19,8 @@ package org.jetbrains.kotlin.codegen.optimization.boxing
import org.jetbrains.kotlin.codegen.optimization.OptimizationMethodVisitor
import org.jetbrains.kotlin.codegen.optimization.common.debugText
import org.jetbrains.kotlin.codegen.optimization.common.isLoadOperation
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.fixStack.peekWords
import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
@@ -32,59 +30,49 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue
import java.util.*
private typealias Transformation = (AbstractInsnNode) -> Unit
class PopBackwardPropagationTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) {
if (!OptimizationMethodVisitor.canBeOptimizedUsingSourceInterpreter(methodNode)) return
Transformer(methodNode).transform()
if (methodNode.instructions.any { it.isPop() || it.isPurePush() }) {
Transformer(methodNode).transform()
}
}
private class Transformer(val methodNode: MethodNode) {
private interface Transformation {
fun apply(insn: AbstractInsnNode)
}
private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation =
object : Transformation {
override fun apply(insn: AbstractInsnNode) {
body(insn)
}
}
private val REPLACE_WITH_NOP = Transformation { insnList.set(it, createRemovableNopInsn()) }
private val REPLACE_WITH_POP1 = Transformation { insnList.set(it, InsnNode(Opcodes.POP)) }
private val REPLACE_WITH_POP2 = Transformation { insnList.set(it, InsnNode(Opcodes.POP2)) }
private val INSERT_POP1_AFTER = Transformation { insnList.insert(it, InsnNode(Opcodes.POP)) }
private val INSERT_POP2_AFTER = Transformation { insnList.insert(it, InsnNode(Opcodes.POP2)) }
private val REPLACE_WITH_NOP: Transformation = { insnList.set(it, InsnNode(Opcodes.NOP)) }
private val REPLACE_WITH_POP1: Transformation = { insnList.set(it, InsnNode(Opcodes.POP)) }
private val REPLACE_WITH_POP2: Transformation = { insnList.set(it, InsnNode(Opcodes.POP2)) }
private val INSERT_POP1_AFTER: Transformation = { insnList.insert(it, InsnNode(Opcodes.POP)) }
private val INSERT_POP2_AFTER: Transformation = { insnList.insert(it, InsnNode(Opcodes.POP2)) }
private val insnList = methodNode.instructions
private val insns = insnList.toArray()
private val dontTouchInsnIndices = BitSet(insns.size)
private val transformations = hashMapOf<AbstractInsnNode, Transformation>()
private val removableNops = hashSetOf<InsnNode>()
private val frames by lazy { analyzeMethodBody() }
private val transformations = hashMapOf<AbstractInsnNode, Transformation>()
private val frames = analyzeMethodBody()
fun transform() {
if (insns.none { it.isPop() || it.isPurePush() }) return
computeTransformations()
for ((insn, transformation) in transformations.entries) {
transformation.apply(insn)
for ((i, insn) in insns.withIndex()) {
if (insn.opcode == Opcodes.POP && frames[i] != null) {
val inputTop = getInputTop(insn)
val sources = inputTop.insns
if (sources.all { !isDontTouch(it) } && sources.any { isTransformablePopOperand(it) }) {
transformations[insn] = REPLACE_WITH_NOP
sources.forEach { propagatePopBackwards(it, inputTop.size) }
}
}
}
for ((insn, transformation) in transformations.entries) {
transformation(insn)
}
postprocessNops()
}
private fun analyzeMethodBody(): Array<out Frame<SourceValue>?> {
val frames = Analyzer<SourceValue>(HazardsTrackingInterpreter()).analyze("fake", methodNode)
postprocessStackHazards(frames)
return frames
}
private fun postprocessStackHazards(frames: Array<out Frame<SourceValue>?>) {
val frames = Analyzer(HazardsTrackingInterpreter()).analyze("fake", methodNode)
val insns = methodNode.instructions.toArray()
for (i in frames.indices) {
val frame = frames[i] ?: continue
@@ -113,6 +101,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
}
}
}
return frames
}
private fun throwIncorrectBytecode(insn: AbstractInsnNode?, frame: Frame<SourceValue>): Nothing {
@@ -164,39 +153,16 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
}
}
private fun computeTransformations() {
transformations.clear()
for (i in insns.indices) {
if (frames[i] == null) continue
val insn = insns[i]
if (insn.opcode == Opcodes.POP) {
propagatePopBackwards(insn, 0)
}
}
}
private fun propagatePopBackwards(insn: AbstractInsnNode, poppedValueSize: Int) {
if (transformations.containsKey(insn)) return
when {
insn.opcode == Opcodes.POP -> {
val inputTop = getInputTop(insn)
val sources = inputTop.insns
if (sources.all { !isDontTouch(it) } && sources.any { isTransformablePopOperand(it) }) {
transformations[insn] = replaceWithNopTransformation()
sources.forEach { propagatePopBackwards(it, inputTop.size) }
}
}
insn.opcode == Opcodes.CHECKCAST -> {
val inputTop = getInputTop(insn)
val sources = inputTop.insns
val resultType = (insn as TypeInsnNode).desc
if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) {
transformations[insn] = replaceWithNopTransformation()
transformations[insn] = REPLACE_WITH_NOP
sources.forEach { propagatePopBackwards(it, inputTop.size) }
} else {
transformations[insn] = insertPopAfterTransformation(poppedValueSize)
@@ -204,22 +170,21 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
}
insn.isPrimitiveBoxing() -> {
val boxedValueSize = getInputTop(insn).size
transformations[insn] = replaceWithPopTransformation(boxedValueSize)
transformations[insn] = replaceWithPopTransformation(getInputTop(insn).size)
}
insn.isPurePush() -> {
transformations[insn] = replaceWithNopTransformation()
transformations[insn] = REPLACE_WITH_NOP
}
insn.isPrimitiveTypeConversion() -> {
val inputTop = getInputTop(insn)
val sources = inputTop.insns
if (sources.all { !isDontTouch(it) }) {
transformations[insn] = replaceWithNopTransformation()
transformations[insn] = REPLACE_WITH_NOP
sources.forEach { propagatePopBackwards(it, inputTop.size) }
} else {
transformations[insn] = replaceWithPopTransformation(poppedValueSize)
transformations[insn] = replaceWithPopTransformation(inputTop.size)
}
}
@@ -229,39 +194,6 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
}
}
private fun postprocessNops() {
var node: AbstractInsnNode? = insnList.first
var hasRemovableNops = false
while (node != null) {
node = node.next
val begin = node ?: break
while (node != null && node !is LabelNode) {
if (node in removableNops) {
hasRemovableNops = true
}
node = node.next
}
val end = node
if (hasRemovableNops) {
removeUnneededNopsInRange(begin, end)
}
hasRemovableNops = false
}
}
private fun removeUnneededNopsInRange(begin: AbstractInsnNode, end: AbstractInsnNode?) {
var node: AbstractInsnNode? = begin
var keepNop = true
while (node != null && node != end) {
if (node in removableNops && !keepNop) {
node = insnList.removeNodeGetNext(node)
} else {
if (node.isMeaningful) keepNop = false
node = node.next
}
}
}
private fun replaceWithPopTransformation(size: Int): Transformation =
when (size) {
1 -> REPLACE_WITH_POP1
@@ -276,12 +208,6 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
else -> throw AssertionError("Unexpected pop value size: $size")
}
private fun replaceWithNopTransformation(): Transformation =
REPLACE_WITH_NOP
private fun createRemovableNopInsn() =
InsnNode(Opcodes.NOP).apply { removableNops.add(this) }
private fun getInputTop(insn: AbstractInsnNode): SourceValue {
val i = insnList.indexOf(insn)
val frame = frames[i] ?: throw AssertionError("Unexpected dead instruction #$i")
@@ -301,16 +227,13 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
}
fun AbstractInsnNode.isPurePush() =
isLoadOperation() ||
opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 ||
isUnitInstance()
isLoadOperation() || opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 || isUnitInstance()
fun AbstractInsnNode.isPop() =
opcode == Opcodes.POP || opcode == Opcodes.POP2
fun AbstractInsnNode.isUnitInstance() =
opcode == Opcodes.GETSTATIC &&
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
opcode == Opcodes.GETSTATIC && this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
fun AbstractInsnNode.isPrimitiveTypeConversion() =
opcode in Opcodes.I2L..Opcodes.I2S