Minor: reformat code in org.jetbrains.kotlin.codegen.optimization

This commit is contained in:
Dmitry Petrov
2018-01-24 14:13:36 +03:00
parent e12deb54d6
commit bd25bf14df
35 changed files with 645 additions and 652 deletions
@@ -46,10 +46,10 @@ class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion
val atLeastVersion = MavenComparableVersion("$epic.$major.$minor") val atLeastVersion = MavenComparableVersion("$epic.$major.$minor")
val replacementInsn = val replacementInsn =
if (targetApiVersion.version >= atLeastVersion) if (targetApiVersion.version >= atLeastVersion)
InsnNode(Opcodes.ICONST_1) InsnNode(Opcodes.ICONST_1)
else else
InsnNode(Opcodes.ICONST_0) InsnNode(Opcodes.ICONST_0)
methodNode.instructions.run { methodNode.instructions.run {
remove(prev1) remove(prev1)
@@ -65,29 +65,29 @@ class ApiVersionCallsPreprocessingMethodTransformer(private val targetApiVersion
} }
private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean = private fun AbstractInsnNode.isApiVersionIsAtLeastCall(): Boolean =
isMethodInsnWith(Opcodes.INVOKESTATIC) { isMethodInsnWith(Opcodes.INVOKESTATIC) {
owner.startsWith("kotlin/internal") && owner.startsWith("kotlin/internal") &&
name == "apiVersionIsAtLeast" && name == "apiVersionIsAtLeast" &&
desc == "(III)Z" desc == "(III)Z"
} }
private fun AbstractInsnNode.getIntConstValue(): Int? = private fun AbstractInsnNode.getIntConstValue(): Int? =
when (this) { when (this) {
is InsnNode -> is InsnNode ->
if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5) if (opcode in Opcodes.ICONST_M1..Opcodes.ICONST_5)
opcode - Opcodes.ICONST_0 opcode - Opcodes.ICONST_0
else else
null null
is IntInsnNode -> is IntInsnNode ->
when (opcode) { when (opcode) {
Opcodes.BIPUSH -> operand Opcodes.BIPUSH -> operand
Opcodes.SIPUSH -> operand Opcodes.SIPUSH -> operand
else -> null else -> null
} }
is LdcInsnNode -> cst as? Int is LdcInsnNode -> cst as? Int
else -> null else -> null
} }
} }
@@ -17,13 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.builtins.PrimitiveType import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.codegen.optimization.common.ProperTrackedReferenceValue import org.jetbrains.kotlin.codegen.optimization.common.*
import org.jetbrains.kotlin.codegen.optimization.common.ReferenceTrackingInterpreter
import org.jetbrains.kotlin.codegen.optimization.common.ReferenceValueDescriptor
import org.jetbrains.kotlin.codegen.optimization.common.TrackedReferenceValue
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
@@ -71,10 +65,10 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
var cleanVarInstruction: VarInsnNode? = null var cleanVarInstruction: VarInsnNode? = null
fun canRewrite(): Boolean = fun canRewrite(): Boolean =
!hazard && !hazard &&
initCallInsn != null && initCallInsn != null &&
localVar != null && localVar != null &&
localVarIndex >= 0 localVarIndex >= 0
override fun onUseAsTainted() { override fun onUseAsTainted() {
hazard = true hazard = true
@@ -118,10 +112,10 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
private inner class Interpreter : ReferenceTrackingInterpreter() { private inner class Interpreter : ReferenceTrackingInterpreter() {
override fun newOperation(insn: AbstractInsnNode): BasicValue = override fun newOperation(insn: AbstractInsnNode): BasicValue =
refValuesByNewInsn[insn]?.let { descriptor -> refValuesByNewInsn[insn]?.let { descriptor ->
ProperTrackedReferenceValue(descriptor.refType, descriptor) ProperTrackedReferenceValue(descriptor.refType, descriptor)
} }
?: super.newOperation(insn) ?: super.newOperation(insn)
override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) { override fun processRefValueUsage(value: TrackedReferenceValue, insn: AbstractInsnNode, position: Int) {
for (descriptor in value.descriptors) { for (descriptor in value.descriptors) {
@@ -179,7 +173,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
} }
private fun BasicValue.getCapturedVarOrNull() = private fun BasicValue.getCapturedVarOrNull() =
safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>() safeAs<ProperTrackedReferenceValue>()?.descriptor?.safeAs<CapturedVarDescriptor>()
private fun assignLocalVars() { private fun assignLocalVars() {
for (localVar in methodNode.localVariables) { for (localVar in methodNode.localVariables) {
@@ -195,8 +189,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
if (descriptor.localVar == null) { if (descriptor.localVar == null) {
descriptor.localVar = localVar descriptor.localVar = localVar
} } else {
else {
descriptor.hazard = true descriptor.hazard = true
} }
} }
@@ -210,8 +203,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
refValue.localVarIndex = methodNode.maxLocals refValue.localVarIndex = methodNode.maxLocals
methodNode.maxLocals += 2 methodNode.maxLocals += 2
localVar.index = refValue.localVarIndex localVar.index = refValue.localVarIndex
} } else {
else {
refValue.localVarIndex = localVar.index refValue.localVarIndex = localVar.index
} }
@@ -223,7 +215,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
} }
val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions) val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions)
if (cleanInstructions.size > 1 ) { if (cleanInstructions.size > 1) {
refValue.hazard = true refValue.hazard = true
continue continue
} }
@@ -235,12 +227,14 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
return InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter { return InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter {
it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex it.opcode == Opcodes.ASTORE && it.`var` == oldVarIndex
}.filter { }.filter {
it.previous?.opcode == Opcodes.ACONST_NULL it.previous?.opcode == Opcodes.ACONST_NULL
}.filter { }.filter {
val operationIndex = instructions.indexOf(it) val operationIndex = instructions.indexOf(it)
val localVariableNode = refValue.localVar!! val localVariableNode = refValue.localVar!!
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(localVariableNode.end) instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(
}.toList() localVariableNode.end
)
}.toList()
} }
private fun rewrite() { private fun rewrite() {
@@ -48,20 +48,20 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
} }
private fun collectRewriteActions(): List<() -> Unit> = private fun collectRewriteActions(): List<() -> Unit> =
arrayListOf<() -> Unit>().also { actions -> arrayListOf<() -> Unit>().also { actions ->
val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter()) val frames = analyze(internalClassName, methodNode, ConstantPropagationInterpreter())
val insns = methodNode.instructions.toArray() val insns = methodNode.instructions.toArray()
for (i in frames.indices) { for (i in frames.indices) {
val frame = frames[i] ?: continue val frame = frames[i] ?: continue
val insn = insns[i] as? JumpInsnNode ?: continue val insn = insns[i] as? JumpInsnNode ?: continue
when (insn.opcode) { when (insn.opcode) {
in Opcodes.IFEQ .. Opcodes.IFLE -> in Opcodes.IFEQ..Opcodes.IFLE ->
tryRewriteComparisonWithZero(insn, frame, actions) tryRewriteComparisonWithZero(insn, frame, actions)
in Opcodes.IF_ICMPEQ .. Opcodes.IF_ICMPLE -> in Opcodes.IF_ICMPEQ..Opcodes.IF_ICMPLE ->
tryRewriteBinaryComparison(insn, frame, actions) tryRewriteBinaryComparison(insn, frame, actions)
}
} }
} }
}
private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) { private fun tryRewriteComparisonWithZero(insn: JumpInsnNode, frame: Frame<BasicValue>, actions: ArrayList<() -> Unit>) {
val top = frame.top()!!.safeAs<IConstValue>() ?: return val top = frame.top()!!.safeAs<IConstValue>() ?: return
@@ -93,8 +93,7 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
if (arg1 is IConstValue && arg2 is IConstValue) { if (arg1 is IConstValue && arg2 is IConstValue) {
rewriteBinaryComparisonOfConsts(insn, arg1.value, arg2.value, actions) rewriteBinaryComparisonOfConsts(insn, arg1.value, arg2.value, actions)
} } else if (arg2 is IConstValue && arg2.value == 0) {
else if (arg2 is IConstValue && arg2.value == 0) {
rewriteBinaryComparisonWith0(insn, actions) rewriteBinaryComparisonWith0(insn, actions)
} }
} }
@@ -144,8 +143,8 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
private class IConstValue private constructor(val value: Int) : StrictBasicValue(Type.INT_TYPE) { private class IConstValue private constructor(val value: Int) : StrictBasicValue(Type.INT_TYPE) {
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other === this || other === this ||
other is IConstValue && other.value == this.value other is IConstValue && other.value == this.value
override fun hashCode(): Int = value override fun hashCode(): Int = value
@@ -155,34 +154,34 @@ class ConstantConditionEliminationMethodTransformer : MethodTransformer() {
private val ICONST_CACHE = Array(7) { IConstValue(it - 1) } private val ICONST_CACHE = Array(7) { IConstValue(it - 1) }
fun of(value: Int) = fun of(value: Int) =
if (value in -1 .. 5) if (value in -1..5)
ICONST_CACHE[value + 1] ICONST_CACHE[value + 1]
else else
IConstValue(value) IConstValue(value)
} }
} }
private class ConstantPropagationInterpreter : OptimizationBasicInterpreter() { private class ConstantPropagationInterpreter : OptimizationBasicInterpreter() {
override fun newOperation(insn: AbstractInsnNode): BasicValue = override fun newOperation(insn: AbstractInsnNode): BasicValue =
when (insn.opcode) { when (insn.opcode) {
in Opcodes.ICONST_M1 .. Opcodes.ICONST_5 -> in Opcodes.ICONST_M1..Opcodes.ICONST_5 ->
IConstValue.of(insn.opcode - Opcodes.ICONST_0) IConstValue.of(insn.opcode - Opcodes.ICONST_0)
Opcodes.BIPUSH, Opcodes.SIPUSH -> Opcodes.BIPUSH, Opcodes.SIPUSH ->
IConstValue.of(insn.cast<IntInsnNode>().operand) IConstValue.of(insn.cast<IntInsnNode>().operand)
Opcodes.LDC -> { Opcodes.LDC -> {
val operand = insn.cast<LdcInsnNode>().cst val operand = insn.cast<LdcInsnNode>().cst
if (operand is Int) if (operand is Int)
IConstValue.of(operand) IConstValue.of(operand)
else else
super.newOperation(insn) super.newOperation(insn)
}
else -> super.newOperation(insn)
} }
else -> super.newOperation(insn)
}
override fun merge(v: BasicValue, w: BasicValue): BasicValue = override fun merge(v: BasicValue, w: BasicValue): BasicValue =
if (v is IConstValue && w is IConstValue && v == w) if (v is IConstValue && w is IConstValue && v == w)
v v
else else
super.merge(v, w) super.merge(v, w)
} }
} }
@@ -55,15 +55,15 @@ class DeadCodeEliminationMethodTransformer : MethodTransformer() {
} }
private fun shouldRemove(insn: AbstractInsnNode, index: Int, frames: Array<out Any?>): Boolean = private fun shouldRemove(insn: AbstractInsnNode, index: Int, frames: Array<out Any?>): Boolean =
when (insn) { when (insn) {
is LabelNode -> is LabelNode ->
// Do not remove label nodes because they can be referred by try/catch blocks or local variables table // Do not remove label nodes because they can be referred by try/catch blocks or local variables table
false false
is LineNumberNode -> is LineNumberNode ->
isDeadLineNumber(insn, index, frames) isDeadLineNumber(insn, index, frames)
else -> else ->
frames[index] == null frames[index] == null
} }
private fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean { private fun isDeadLineNumber(insn: LineNumberNode, index: Int, frames: Array<out Any?>): Boolean {
// Line number node is "dead" if the corresponding line number interval // Line number node is "dead" if the corresponding line number interval
@@ -18,10 +18,8 @@ package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer import org.jetbrains.kotlin.codegen.optimization.fixStack.FixStackMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.CompositeMethodTransformer
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class FixStackWithLabelNormalizationMethodTransformer : CompositeMethodTransformer( class FixStackWithLabelNormalizationMethodTransformer : CompositeMethodTransformer(
LabelNormalizationMethodTransformer(), LabelNormalizationMethodTransformer(),
FixStackMethodTransformer() FixStackMethodTransformer()
) )
@@ -49,13 +49,11 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
newLabelNodes[thisNode.label] = prevNode newLabelNodes[thisNode.label] = prevNode
removedAnyLabels = true removedAnyLabels = true
thisNode = instructions.removeNodeGetNext(thisNode) thisNode = instructions.removeNodeGetNext(thisNode)
} } else {
else {
newLabelNodes[thisNode.label] = thisNode newLabelNodes[thisNode.label] = thisNode
thisNode = thisNode.next thisNode = thisNode.next
} }
} } else {
else {
thisNode = thisNode.next thisNode = thisNode.next
} }
} }
@@ -83,19 +81,19 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
} }
private fun rewriteLineNumberNode(oldLineNode: LineNumberNode): AbstractInsnNode? = private fun rewriteLineNumberNode(oldLineNode: LineNumberNode): AbstractInsnNode? =
instructions.replaceNodeGetNext(oldLineNode, oldLineNode.rewriteLabels()) instructions.replaceNodeGetNext(oldLineNode, oldLineNode.rewriteLabels())
private fun rewriteJumpInsn(oldJumpNode: JumpInsnNode): AbstractInsnNode? = private fun rewriteJumpInsn(oldJumpNode: JumpInsnNode): AbstractInsnNode? =
instructions.replaceNodeGetNext(oldJumpNode, oldJumpNode.rewriteLabels()) instructions.replaceNodeGetNext(oldJumpNode, oldJumpNode.rewriteLabels())
private fun rewriteLookupSwitchInsn(oldSwitchNode: LookupSwitchInsnNode): AbstractInsnNode? = private fun rewriteLookupSwitchInsn(oldSwitchNode: LookupSwitchInsnNode): AbstractInsnNode? =
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels()) instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
private fun rewriteTableSwitchInsn(oldSwitchNode: TableSwitchInsnNode): AbstractInsnNode? = private fun rewriteTableSwitchInsn(oldSwitchNode: TableSwitchInsnNode): AbstractInsnNode? =
instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels()) instructions.replaceNodeGetNext(oldSwitchNode, oldSwitchNode.rewriteLabels())
private fun rewriteFrameNode(oldFrameNode: FrameNode): AbstractInsnNode? = private fun rewriteFrameNode(oldFrameNode: FrameNode): AbstractInsnNode? =
instructions.replaceNodeGetNext(oldFrameNode, oldFrameNode.rewriteLabels()) instructions.replaceNodeGetNext(oldFrameNode, oldFrameNode.rewriteLabels())
private fun rewriteTryCatchBlocks() { private fun rewriteTryCatchBlocks() {
methodNode.tryCatchBlocks = methodNode.tryCatchBlocks.map { oldTcb -> methodNode.tryCatchBlocks = methodNode.tryCatchBlocks.map { oldTcb ->
@@ -109,21 +107,21 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
private fun rewriteLocalVars() { private fun rewriteLocalVars() {
methodNode.localVariables = methodNode.localVariables.map { oldVar -> methodNode.localVariables = methodNode.localVariables.map { oldVar ->
LocalVariableNode( LocalVariableNode(
oldVar.name, oldVar.name,
oldVar.desc, oldVar.desc,
oldVar.signature, oldVar.signature,
getNew(oldVar.start), getNew(oldVar.start),
getNew(oldVar.end), getNew(oldVar.end),
oldVar.index oldVar.index
) )
} }
} }
private fun LineNumberNode.rewriteLabels(): AbstractInsnNode = private fun LineNumberNode.rewriteLabels(): AbstractInsnNode =
LineNumberNode(line, getNewOrOld(start)) LineNumberNode(line, getNewOrOld(start))
private fun JumpInsnNode.rewriteLabels(): AbstractInsnNode = private fun JumpInsnNode.rewriteLabels(): AbstractInsnNode =
JumpInsnNode(opcode, getNew(label)) JumpInsnNode(opcode, getNew(label))
private fun LookupSwitchInsnNode.rewriteLabels(): AbstractInsnNode { private fun LookupSwitchInsnNode.rewriteLabels(): AbstractInsnNode {
val switchNode = LookupSwitchInsnNode(getNew(dflt), keys.toIntArray(), emptyArray()) val switchNode = LookupSwitchInsnNode(getNew(dflt), keys.toIntArray(), emptyArray())
@@ -145,10 +143,10 @@ class LabelNormalizationMethodTransformer : MethodTransformer() {
} }
private fun getNew(oldLabelNode: LabelNode): LabelNode = private fun getNew(oldLabelNode: LabelNode): LabelNode =
newLabelNodes[oldLabelNode.label]!! newLabelNodes[oldLabelNode.label]!!
private fun getNewOrOld(oldLabelNode: LabelNode): LabelNode = private fun getNewOrOld(oldLabelNode: LabelNode): LabelNode =
newLabelNodes[oldLabelNode.label] ?: oldLabelNode newLabelNodes[oldLabelNode.label] ?: oldLabelNode
} }
} }
@@ -24,8 +24,7 @@ class MethodVerifier(private val checkPoint: String) : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) { override fun transform(internalClassName: String, methodNode: MethodNode) {
try { try {
analyze(internalClassName, methodNode, BasicVerifier()) analyze(internalClassName, methodNode, BasicVerifier())
} } catch (e: Throwable) {
catch (e: Throwable) {
throw AssertionError("$checkPoint: incorrect bytecode", e) throw AssertionError("$checkPoint: incorrect bytecode", e)
} }
} }
@@ -28,17 +28,17 @@ import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
class OptimizationMethodVisitor( class OptimizationMethodVisitor(
delegate: MethodVisitor, delegate: MethodVisitor,
private val disableOptimization: Boolean, private val disableOptimization: Boolean,
private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode, private val constructorCallNormalizationMode: JVMConstructorCallNormalizationMode,
access: Int, access: Int,
name: String, name: String,
desc: String, desc: String,
signature: String?, signature: String?,
exceptions: Array<String>? exceptions: Array<String>?
) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) { ) : TransformationMethodVisitor(delegate, access, name, desc, signature, exceptions) {
private val constructorCallNormalizationTransformer = private val constructorCallNormalizationTransformer =
UninitializedStoresMethodTransformer(constructorCallNormalizationMode) UninitializedStoresMethodTransformer(constructorCallNormalizationMode)
override fun performTransformations(methodNode: MethodNode) { override fun performTransformations(methodNode: MethodNode) {
normalizationMethodTransformer.transform("fake", methodNode) normalizationMethodTransformer.transform("fake", methodNode)
@@ -55,22 +55,22 @@ class OptimizationMethodVisitor(
private val MEMORY_LIMIT_BY_METHOD_MB = 50 private val MEMORY_LIMIT_BY_METHOD_MB = 50
val normalizationMethodTransformer = CompositeMethodTransformer( val normalizationMethodTransformer = CompositeMethodTransformer(
FixStackWithLabelNormalizationMethodTransformer(), FixStackWithLabelNormalizationMethodTransformer(),
MethodVerifier("AFTER mandatory stack transformations") MethodVerifier("AFTER mandatory stack transformations")
) )
val optimizationTransformer = CompositeMethodTransformer( val optimizationTransformer = CompositeMethodTransformer(
CapturedVarsOptimizationMethodTransformer(), CapturedVarsOptimizationMethodTransformer(),
RedundantNullCheckMethodTransformer(), RedundantNullCheckMethodTransformer(),
RedundantCheckCastEliminationMethodTransformer(), RedundantCheckCastEliminationMethodTransformer(),
ConstantConditionEliminationMethodTransformer(), ConstantConditionEliminationMethodTransformer(),
RedundantBoxingMethodTransformer(), RedundantBoxingMethodTransformer(),
StackPeepholeOptimizationsTransformer(), StackPeepholeOptimizationsTransformer(),
PopBackwardPropagationTransformer(), PopBackwardPropagationTransformer(),
DeadCodeEliminationMethodTransformer(), DeadCodeEliminationMethodTransformer(),
RedundantGotoMethodTransformer(), RedundantGotoMethodTransformer(),
RedundantNopsCleanupMethodTransformer(), RedundantNopsCleanupMethodTransformer(),
MethodVerifier("AFTER optimizations") MethodVerifier("AFTER optimizations")
) )
fun canBeOptimized(node: MethodNode): Boolean { fun canBeOptimized(node: MethodNode): Boolean {
@@ -22,7 +22,8 @@ import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
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.MethodNode
import org.jetbrains.org.objectweb.asm.tree.TypeInsnNode
class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() { class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) { override fun transform(internalClassName: String, methodNode: MethodNode) {
@@ -57,7 +58,7 @@ class RedundantCheckCastEliminationMethodTransformer : MethodTransformer() {
} }
private fun isTrivialSubtype(superType: Type, subType: Type) = private fun isTrivialSubtype(superType: Type, subType: Type) =
superType == subType superType == subType
private fun isMultiArrayType(type: Type) = type.sort == Type.ARRAY && type.dimensions != 1 private fun isMultiArrayType(type: Type) = type.sort == Type.ARRAY && type.dimensions != 1
} }
@@ -16,13 +16,13 @@
package org.jetbrains.kotlin.codegen.optimization package org.jetbrains.kotlin.codegen.optimization
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful import org.jetbrains.kotlin.codegen.optimization.common.isMeaningful
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
class RedundantGotoMethodTransformer : MethodTransformer() { class RedundantGotoMethodTransformer : MethodTransformer() {
/** /**
@@ -38,7 +38,7 @@ class RedundantGotoMethodTransformer : MethodTransformer() {
insn is LabelNode -> insn is LabelNode ->
currentLabels.add(insn) currentLabels.add(insn)
insn.opcode == Opcodes.GOTO && insn.opcode == Opcodes.GOTO &&
(insn as JumpInsnNode).label in currentLabels -> (insn as JumpInsnNode).label in currentLabels ->
insnsToRemove.add(insn) insnsToRemove.add(insn)
insn.isMeaningful -> insn.isMeaningful ->
currentLabels.clear() currentLabels.clear()
@@ -46,8 +46,7 @@ class RedundantNopsCleanupMethodTransformer : MethodTransformer() {
val toRemove = current val toRemove = current
current = current.next current = current.next
methodNode.instructions.remove(toRemove) methodNode.instructions.remove(toRemove)
} } else {
else {
current = current.next current = current.next
} }
} }
@@ -84,7 +83,7 @@ class RedundantNopsCleanupMethodTransformer : MethodTransformer() {
} }
for (i in 0 .. localVariableLabels.size - 2) { for (i in 0..localVariableLabels.size - 2) {
val begin = localVariableLabels[i] val begin = localVariableLabels[i]
val end = localVariableLabels[i + 1] val end = localVariableLabels[i + 1]
if (InsnSequence(begin, end).any { it in requiredNops }) continue if (InsnSequence(begin, end).any { it in requiredNops }) continue
@@ -109,8 +108,7 @@ internal fun getRequiredNopInRange(firstInclusive: AbstractInsnNode, lastExclusi
while (current != null && current != lastExclusive) { while (current != null && current != lastExclusive) {
if (current.isMeaningful && current.opcode != Opcodes.NOP) { if (current.isMeaningful && current.opcode != Opcodes.NOP) {
return null return null
} } else if (current.opcode == Opcodes.NOP) {
else if (current.opcode == Opcodes.NOP) {
lastNop = current lastNop = current
} }
current = current.next current = current.next
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.config.JVMConstructorCallNormalizationMode
import org.jetbrains.org.objectweb.asm.tree.MethodNode import org.jetbrains.org.objectweb.asm.tree.MethodNode
class UninitializedStoresMethodTransformer( class UninitializedStoresMethodTransformer(
private val mode: JVMConstructorCallNormalizationMode private val mode: JVMConstructorCallNormalizationMode
) : MethodTransformer() { ) : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) { override fun transform(internalClassName: String, methodNode: MethodNode) {
@@ -34,9 +34,9 @@ abstract class BoxedBasicValue(type: Type) : StrictBasicValue(type) {
class CleanBoxedValue( class CleanBoxedValue(
boxedType: Type, boxedType: Type,
boxingInsn: AbstractInsnNode, boxingInsn: AbstractInsnNode,
progressionIterator: ProgressionIteratorBasicValue? progressionIterator: ProgressionIteratorBasicValue?
) : BoxedBasicValue(boxedType) { ) : BoxedBasicValue(boxedType) {
override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator) override val descriptor = BoxedValueDescriptor(boxedType, boxingInsn, progressionIterator)
@@ -53,9 +53,9 @@ class TaintedBoxedValue(private val boxedBasicValue: CleanBoxedValue) : BoxedBas
class BoxedValueDescriptor( class BoxedValueDescriptor(
private val boxedType: Type, private val boxedType: Type,
val boxingInsn: AbstractInsnNode, val boxingInsn: AbstractInsnNode,
val progressionIterator: ProgressionIteratorBasicValue? val progressionIterator: ProgressionIteratorBasicValue?
) { ) {
private val associatedInsns = HashSet<AbstractInsnNode>() private val associatedInsns = HashSet<AbstractInsnNode>()
private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>() private val unboxingWithCastInsns = HashSet<Pair<AbstractInsnNode, Type>>()
@@ -76,14 +76,14 @@ class BoxedValueDescriptor(
} }
fun getVariablesIndexes(): List<Int> = fun getVariablesIndexes(): List<Int> =
ArrayList(associatedVariables) ArrayList(associatedVariables)
fun addMergedWith(descriptor: BoxedValueDescriptor) { fun addMergedWith(descriptor: BoxedValueDescriptor) {
mergedWith.add(descriptor) mergedWith.add(descriptor)
} }
fun getMergedWith(): Iterable<BoxedValueDescriptor> = fun getMergedWith(): Iterable<BoxedValueDescriptor> =
mergedWith mergedWith
fun markAsUnsafeToRemove() { fun markAsUnsafeToRemove() {
isSafeToRemove = false isSafeToRemove = false
@@ -98,7 +98,7 @@ class BoxedValueDescriptor(
} }
fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> = fun getUnboxingWithCastInsns(): Set<Pair<AbstractInsnNode, Type>> =
unboxingWithCastInsns unboxingWithCastInsns
} }
@@ -36,12 +36,16 @@ import java.util.*
open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() { open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasicInterpreter() {
private val boxingPlaces = HashMap<Int, BoxedBasicValue>() private val boxingPlaces = HashMap<Int, BoxedBasicValue>()
protected open fun createNewBoxing(insn: AbstractInsnNode, type: Type, progressionIterator: ProgressionIteratorBasicValue?): BasicValue = protected open fun createNewBoxing(
boxingPlaces.getOrPut(insnList.indexOf(insn)) { insn: AbstractInsnNode,
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator) type: Type,
onNewBoxedValue(boxedBasicValue) progressionIterator: ProgressionIteratorBasicValue?
boxedBasicValue ): BasicValue =
} boxingPlaces.getOrPut(insnList.indexOf(insn)) {
val boxedBasicValue = CleanBoxedValue(type, insn, progressionIterator)
onNewBoxedValue(boxedBasicValue)
boxedBasicValue
}
protected fun checkUsedValue(value: BasicValue) { protected fun checkUsedValue(value: BasicValue) {
if (value is TaintedBoxedValue) { if (value is TaintedBoxedValue) {
@@ -69,7 +73,7 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type) ProgressionIteratorBasicValue.byProgressionClassType(firstArg.type)
insn.isNextMethodCallOfProgressionIterator(values) -> { insn.isNextMethodCallOfProgressionIterator(values) -> {
val progressionIterator = firstArg as? ProgressionIteratorBasicValue val progressionIterator = firstArg as? ProgressionIteratorBasicValue
?: throw AssertionError("firstArg should be progression iterator") ?: throw AssertionError("firstArg should be progression iterator")
createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator) createNewBoxing(insn, AsmUtil.boxType(progressionIterator.valuesPrimitiveType), progressionIterator)
} }
insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> { insn.isAreEqualIntrinsicForSameTypedBoxedValues(values) && canValuesBeUnboxedForAreEqual(values) -> {
@@ -104,30 +108,30 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
} }
protected open fun isExactValue(value: BasicValue) = protected open fun isExactValue(value: BasicValue) =
value is ProgressionIteratorBasicValue || value is ProgressionIteratorBasicValue ||
value is CleanBoxedValue || value is CleanBoxedValue ||
value.type != null && isProgressionClass(value.type) value.type != null && isProgressionClass(value.type)
override fun merge(v: BasicValue, w: BasicValue) = override fun merge(v: BasicValue, w: BasicValue) =
when { when {
v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE -> v == StrictBasicValue.UNINITIALIZED_VALUE || w == StrictBasicValue.UNINITIALIZED_VALUE ->
StrictBasicValue.UNINITIALIZED_VALUE StrictBasicValue.UNINITIALIZED_VALUE
v is BoxedBasicValue && w is BoxedBasicValue -> { v is BoxedBasicValue && w is BoxedBasicValue -> {
onMergeSuccess(v, w) onMergeSuccess(v, w)
when { when {
v is TaintedBoxedValue -> v v is TaintedBoxedValue -> v
w is TaintedBoxedValue -> w w is TaintedBoxedValue -> w
v.type != w.type -> v.taint() v.type != w.type -> v.taint()
else -> v else -> v
}
} }
v is BoxedBasicValue ->
v.taint()
w is BoxedBasicValue ->
w.taint()
else ->
super.merge(v, w)
} }
v is BoxedBasicValue ->
v.taint()
w is BoxedBasicValue ->
w.taint()
else ->
super.merge(v, w)
}
protected open fun onNewBoxedValue(value: BoxedBasicValue) {} protected open fun onNewBoxedValue(value: BoxedBasicValue) {}
protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {} protected open fun onUnboxing(insn: AbstractInsnNode, value: BoxedBasicValue, resultType: Type) {}
@@ -140,51 +144,51 @@ open class BoxingInterpreter(private val insnList: InsnList) : OptimizationBasic
} }
private val UNBOXING_METHOD_NAMES = private val UNBOXING_METHOD_NAMES =
ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue") ImmutableSet.of("booleanValue", "charValue", "byteValue", "shortValue", "intValue", "floatValue", "longValue", "doubleValue")
private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE) private val KCLASS_TO_JLCLASS = Type.getMethodDescriptor(AsmTypes.JAVA_CLASS_TYPE, AsmTypes.K_CLASS_TYPE)
private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE) private val JLCLASS_TO_KCLASS = Type.getMethodDescriptor(AsmTypes.K_CLASS_TYPE, AsmTypes.JAVA_CLASS_TYPE)
fun AbstractInsnNode.isUnboxing() = fun AbstractInsnNode.isUnboxing() =
isPrimitiveUnboxing() || isJavaLangClassUnboxing() isPrimitiveUnboxing() || isJavaLangClassUnboxing()
fun AbstractInsnNode.isBoxing() = fun AbstractInsnNode.isBoxing() =
isPrimitiveBoxing() || isJavaLangClassBoxing() isPrimitiveBoxing() || isJavaLangClassBoxing()
fun AbstractInsnNode.isPrimitiveUnboxing() = fun AbstractInsnNode.isPrimitiveUnboxing() =
isMethodInsnWith(Opcodes.INVOKEVIRTUAL) { isMethodInsnWith(Opcodes.INVOKEVIRTUAL) {
isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name) isWrapperClassNameOrNumber(owner) && isUnboxingMethodName(name)
} }
fun AbstractInsnNode.isJavaLangClassUnboxing() = fun AbstractInsnNode.isJavaLangClassUnboxing() =
isMethodInsnWith(Opcodes.INVOKESTATIC) { isMethodInsnWith(Opcodes.INVOKESTATIC) {
owner == "kotlin/jvm/JvmClassMappingKt" && owner == "kotlin/jvm/JvmClassMappingKt" &&
name == "getJavaClass" && name == "getJavaClass" &&
desc == KCLASS_TO_JLCLASS desc == KCLASS_TO_JLCLASS
} }
inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean = inline fun AbstractInsnNode.isMethodInsnWith(opcode: Int, condition: MethodInsnNode.() -> Boolean): Boolean =
this.opcode == opcode && this is MethodInsnNode && this.condition() this.opcode == opcode && this is MethodInsnNode && this.condition()
private fun isWrapperClassNameOrNumber(internalClassName: String) = private fun isWrapperClassNameOrNumber(internalClassName: String) =
isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java) isWrapperClassName(internalClassName) || internalClassName == Type.getInternalName(Number::class.java)
private fun isWrapperClassName(internalClassName: String) = private fun isWrapperClassName(internalClassName: String) =
JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName)) JvmPrimitiveType.isWrapperClassName(buildFqNameByInternal(internalClassName))
private fun buildFqNameByInternal(internalClassName: String) = private fun buildFqNameByInternal(internalClassName: String) =
FqName(Type.getObjectType(internalClassName).className) FqName(Type.getObjectType(internalClassName).className)
private fun isUnboxingMethodName(name: String) = private fun isUnboxingMethodName(name: String) =
UNBOXING_METHOD_NAMES.contains(name) UNBOXING_METHOD_NAMES.contains(name)
fun AbstractInsnNode.isPrimitiveBoxing() = fun AbstractInsnNode.isPrimitiveBoxing() =
isMethodInsnWith(Opcodes.INVOKESTATIC) { isMethodInsnWith(Opcodes.INVOKESTATIC) {
isWrapperClassName(owner) && isWrapperClassName(owner) &&
name == "valueOf" && name == "valueOf" &&
isBoxingMethodDescriptor() isBoxingMethodDescriptor()
} }
private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean { private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
val ownerType = Type.getObjectType(owner) val ownerType = Type.getObjectType(owner)
@@ -192,58 +196,58 @@ private fun MethodInsnNode.isBoxingMethodDescriptor(): Boolean {
} }
fun AbstractInsnNode.isJavaLangClassBoxing() = fun AbstractInsnNode.isJavaLangClassBoxing() =
isMethodInsnWith(Opcodes.INVOKESTATIC) { isMethodInsnWith(Opcodes.INVOKESTATIC) {
owner == AsmTypes.REFLECTION && owner == AsmTypes.REFLECTION &&
name == "getOrCreateKotlinClass" && name == "getOrCreateKotlinClass" &&
desc == JLCLASS_TO_KCLASS desc == JLCLASS_TO_KCLASS
} }
fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) = fun AbstractInsnNode.isNextMethodCallOfProgressionIterator(values: List<BasicValue>) =
values.firstOrNull() is ProgressionIteratorBasicValue && values.firstOrNull() is ProgressionIteratorBasicValue &&
isMethodInsnWith(Opcodes.INVOKEINTERFACE) { isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
name == "next" name == "next"
} }
fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) = fun AbstractInsnNode.isIteratorMethodCallOfProgression(values: List<BasicValue>) =
isMethodInsnWith(Opcodes.INVOKEINTERFACE) { isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
val firstArgType = values.firstOrNull()?.type val firstArgType = values.firstOrNull()?.type
firstArgType != null && firstArgType != null &&
isProgressionClass(firstArgType) && isProgressionClass(firstArgType) &&
name == "iterator" name == "iterator"
} }
fun isProgressionClass(type: Type) = fun isProgressionClass(type: Type) =
isRangeOrProgression(buildFqNameByInternal(type.internalName)) isRangeOrProgression(buildFqNameByInternal(type.internalName))
fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) = fun AbstractInsnNode.isAreEqualIntrinsicForSameTypedBoxedValues(values: List<BasicValue>) =
isAreEqualIntrinsic() && areSameTypedBoxedValues(values) isAreEqualIntrinsic() && areSameTypedBoxedValues(values)
fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean { fun areSameTypedBoxedValues(values: List<BasicValue>): Boolean {
if (values.size != 2) return false if (values.size != 2) return false
val (v1, v2) = values val (v1, v2) = values
return v1 is BoxedBasicValue && return v1 is BoxedBasicValue &&
v2 is BoxedBasicValue && v2 is BoxedBasicValue &&
v1.descriptor.unboxedType == v2.descriptor.unboxedType v1.descriptor.unboxedType == v2.descriptor.unboxedType
} }
fun AbstractInsnNode.isAreEqualIntrinsic() = fun AbstractInsnNode.isAreEqualIntrinsic() =
isMethodInsnWith(Opcodes.INVOKESTATIC) { isMethodInsnWith(Opcodes.INVOKESTATIC) {
name == "areEqual" && name == "areEqual" &&
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
desc == "(Ljava/lang/Object;Ljava/lang/Object;)Z" desc == "(Ljava/lang/Object;Ljava/lang/Object;)Z"
} }
private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE) private val shouldUseEqualsForWrappers = setOf(Type.DOUBLE_TYPE, Type.FLOAT_TYPE, AsmTypes.JAVA_CLASS_TYPE)
fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean = fun canValuesBeUnboxedForAreEqual(values: List<BasicValue>): Boolean =
values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers } values.none { getUnboxedType(it.type) in shouldUseEqualsForWrappers }
fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) = fun AbstractInsnNode.isJavaLangComparableCompareToForSameTypedBoxedValues(values: List<BasicValue>) =
isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values) isJavaLangComparableCompareTo() && areSameTypedBoxedValues(values)
fun AbstractInsnNode.isJavaLangComparableCompareTo() = fun AbstractInsnNode.isJavaLangComparableCompareTo() =
isMethodInsnWith(Opcodes.INVOKEINTERFACE) { isMethodInsnWith(Opcodes.INVOKEINTERFACE) {
name == "compareTo" && name == "compareTo" &&
owner == "java/lang/Comparable" && owner == "java/lang/Comparable" &&
desc == "(Ljava/lang/Object;)I" desc == "(Ljava/lang/Object;)I"
} }
@@ -44,11 +44,11 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
} }
private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation = private inline fun Transformation(crossinline body: (AbstractInsnNode) -> Unit): Transformation =
object : Transformation { object : Transformation {
override fun apply(insn: AbstractInsnNode) { override fun apply(insn: AbstractInsnNode) {
body(insn) body(insn)
}
} }
}
private val REPLACE_WITH_NOP = Transformation { insnList.set(it, createRemovableNopInsn()) } 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_POP1 = Transformation { insnList.set(it, InsnNode(Opcodes.POP)) }
@@ -145,7 +145,12 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
return super.binaryOperation(insn, value1, value2) return super.binaryOperation(insn, value1, value2)
} }
override fun ternaryOperation(insn: AbstractInsnNode, value1: SourceValue, value2: SourceValue, value3: SourceValue): SourceValue { override fun ternaryOperation(
insn: AbstractInsnNode,
value1: SourceValue,
value2: SourceValue,
value3: SourceValue
): SourceValue {
value1.insns.markAsDontTouch() value1.insns.markAsDontTouch()
value2.insns.markAsDontTouch() value2.insns.markAsDontTouch()
value3.insns.markAsDontTouch() value3.insns.markAsDontTouch()
@@ -193,8 +198,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) { if (sources.all { !isDontTouch(it) } && sources.any { isTransformableCheckcastOperand(it, resultType) }) {
transformations[insn] = replaceWithNopTransformation() transformations[insn] = replaceWithNopTransformation()
sources.forEach { propagatePopBackwards(it, inputTop.size) } sources.forEach { propagatePopBackwards(it, inputTop.size) }
} } else {
else {
transformations[insn] = insertPopAfterTransformation(poppedValueSize) transformations[insn] = insertPopAfterTransformation(poppedValueSize)
} }
} }
@@ -214,8 +218,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
if (sources.all { !isDontTouch(it) }) { if (sources.all { !isDontTouch(it) }) {
transformations[insn] = replaceWithNopTransformation() transformations[insn] = replaceWithNopTransformation()
sources.forEach { propagatePopBackwards(it, inputTop.size) } sources.forEach { propagatePopBackwards(it, inputTop.size) }
} } else {
else {
transformations[insn] = replaceWithPopTransformation(poppedValueSize) transformations[insn] = replaceWithPopTransformation(poppedValueSize)
} }
} }
@@ -252,8 +255,7 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
while (node != null && node != end) { while (node != null && node != end) {
if (node in removableNops && !keepNop) { if (node in removableNops && !keepNop) {
node = insnList.removeNodeGetNext(node) node = insnList.removeNodeGetNext(node)
} } else {
else {
if (node.isMeaningful) keepNop = false if (node.isMeaningful) keepNop = false
node = node.next node = node.next
} }
@@ -261,24 +263,24 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
} }
private fun replaceWithPopTransformation(size: Int): Transformation = private fun replaceWithPopTransformation(size: Int): Transformation =
when (size) { when (size) {
1 -> REPLACE_WITH_POP1 1 -> REPLACE_WITH_POP1
2 -> REPLACE_WITH_POP2 2 -> REPLACE_WITH_POP2
else -> throw AssertionError("Unexpected pop value size: $size") else -> throw AssertionError("Unexpected pop value size: $size")
} }
private fun insertPopAfterTransformation(size: Int): Transformation = private fun insertPopAfterTransformation(size: Int): Transformation =
when (size) { when (size) {
1 -> INSERT_POP1_AFTER 1 -> INSERT_POP1_AFTER
2 -> INSERT_POP2_AFTER 2 -> INSERT_POP2_AFTER
else -> throw AssertionError("Unexpected pop value size: $size") else -> throw AssertionError("Unexpected pop value size: $size")
} }
private fun replaceWithNopTransformation(): Transformation = private fun replaceWithNopTransformation(): Transformation =
REPLACE_WITH_NOP REPLACE_WITH_NOP
private fun createRemovableNopInsn() = private fun createRemovableNopInsn() =
InsnNode(Opcodes.NOP).apply { removableNops.add(this) } InsnNode(Opcodes.NOP).apply { removableNops.add(this) }
private fun getInputTop(insn: AbstractInsnNode): SourceValue { private fun getInputTop(insn: AbstractInsnNode): SourceValue {
val i = insnList.indexOf(insn) val i = insnList.indexOf(insn)
@@ -287,28 +289,28 @@ class PopBackwardPropagationTransformer : MethodTransformer() {
} }
private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) = private fun isTransformableCheckcastOperand(it: AbstractInsnNode, resultType: String) =
it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType it.isPrimitiveBoxing() && (it as MethodInsnNode).owner == resultType
private fun isTransformablePopOperand(insn: AbstractInsnNode) = private fun isTransformablePopOperand(insn: AbstractInsnNode) =
insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush() insn.opcode == Opcodes.CHECKCAST || insn.isPrimitiveBoxing() || insn.isPurePush()
private fun isDontTouch(insn: AbstractInsnNode) = private fun isDontTouch(insn: AbstractInsnNode) =
dontTouchInsnIndices[insnList.indexOf(insn)] dontTouchInsnIndices[insnList.indexOf(insn)]
} }
} }
fun AbstractInsnNode.isPurePush() = fun AbstractInsnNode.isPurePush() =
isLoadOperation() || isLoadOperation() ||
opcode in Opcodes.ACONST_NULL .. Opcodes.LDC + 2 || opcode in Opcodes.ACONST_NULL..Opcodes.LDC + 2 ||
isUnitInstance() isUnitInstance()
fun AbstractInsnNode.isPop() = fun AbstractInsnNode.isPop() =
opcode == Opcodes.POP || opcode == Opcodes.POP2 opcode == Opcodes.POP || opcode == Opcodes.POP2
fun AbstractInsnNode.isUnitInstance() = fun AbstractInsnNode.isUnitInstance() =
opcode == Opcodes.GETSTATIC && opcode == Opcodes.GETSTATIC &&
this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE" this is FieldInsnNode && owner == "kotlin/Unit" && name == "INSTANCE"
fun AbstractInsnNode.isPrimitiveTypeConversion() = fun AbstractInsnNode.isPrimitiveTypeConversion() =
opcode in Opcodes.I2L .. Opcodes.I2S opcode in Opcodes.I2L..Opcodes.I2S
@@ -30,6 +30,7 @@ import org.jetbrains.org.objectweb.asm.Type;
public class ProgressionIteratorBasicValue extends StrictBasicValue { public class ProgressionIteratorBasicValue extends StrictBasicValue {
private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE; private final static ImmutableMap<String, Type> VALUES_TYPENAME_TO_TYPE;
static { static {
ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder(); ImmutableMap.Builder<String, Type> builder = ImmutableMap.builder();
for (PrimitiveType primitiveType : RangeCodegenUtilKt.getSupportedRangeTypes()) { for (PrimitiveType primitiveType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
@@ -39,6 +40,7 @@ public class ProgressionIteratorBasicValue extends StrictBasicValue {
} }
private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE; private static final ImmutableMap<PrimitiveType, ProgressionIteratorBasicValue> ITERATOR_VALUE_BY_ELEMENT_PRIMITIVE_TYPE;
static { static {
ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder(); ImmutableMap.Builder<PrimitiveType, ProgressionIteratorBasicValue> builder = ImmutableMap.builder();
for (PrimitiveType elementType : RangeCodegenUtilKt.getSupportedRangeTypes()) { for (PrimitiveType elementType : RangeCodegenUtilKt.getSupportedRangeTypes()) {
@@ -116,8 +116,7 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) { if (!PERMITTED_OPERATIONS_OPCODES.contains(insnNode.opcode)) {
markValueAsDirty(value) markValueAsDirty(value)
} } else {
else {
addAssociatedInsn(value, insnNode) addAssociatedInsn(value, insnNode)
} }
} }
@@ -129,22 +128,22 @@ internal class RedundantBoxingInterpreter(insnList: InsnList) : BoxingInterprete
companion object { companion object {
private val PERMITTED_OPERATIONS_OPCODES = private val PERMITTED_OPERATIONS_OPCODES =
ImmutableSet.of(Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF) ImmutableSet.of(Opcodes.ASTORE, Opcodes.ALOAD, Opcodes.POP, Opcodes.DUP, Opcodes.CHECKCAST, Opcodes.INSTANCEOF)
private val PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER = private val PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER =
ImmutableSet.of(Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE) ImmutableSet.of(Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE)
private fun isSafeCast(value: BoxedBasicValue, targetInternalName: String) = private fun isSafeCast(value: BoxedBasicValue, targetInternalName: String) =
when (targetInternalName) { when (targetInternalName) {
Type.getInternalName(Any::class.java) -> Type.getInternalName(Any::class.java) ->
true true
Type.getInternalName(Number::class.java) -> Type.getInternalName(Number::class.java) ->
PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort) PRIMITIVE_TYPES_SORTS_WITH_WRAPPER_EXTENDS_NUMBER.contains(value.descriptor.unboxedType.sort)
"java/lang/Comparable" -> "java/lang/Comparable" ->
true true
else -> else ->
value.type.internalName == targetInternalName value.type.internalName == targetInternalName
} }
private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) { private fun addAssociatedInsn(value: BoxedBasicValue, insn: AbstractInsnNode) {
value.descriptor.run { value.descriptor.run {
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.codegen.inline.insnOpcodeText
import org.jetbrains.kotlin.codegen.inline.insnText import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue import org.jetbrains.kotlin.codegen.optimization.common.StrictBasicValue
import org.jetbrains.kotlin.codegen.optimization.common.* import org.jetbrains.kotlin.codegen.optimization.common.remapLocalVariables
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
import org.jetbrains.kotlin.codegen.optimization.fixStack.top import org.jetbrains.kotlin.codegen.optimization.fixStack.top
import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer
@@ -32,7 +32,6 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
import org.jetbrains.org.objectweb.asm.tree.analysis.Frame import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import java.util.* import java.util.*
class RedundantBoxingMethodTransformer : MethodTransformer() { class RedundantBoxingMethodTransformer : MethodTransformer() {
@@ -58,9 +57,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun interpretPopInstructionsForBoxedValues( private fun interpretPopInstructionsForBoxedValues(
interpreter: RedundantBoxingInterpreter, interpreter: RedundantBoxingInterpreter,
node: MethodNode, node: MethodNode,
frames: Array<out Frame<BasicValue>?> frames: Array<out Frame<BasicValue>?>
) { ) {
for (i in frames.indices) { for (i in frames.indices) {
val insn = node.instructions[i] val insn = node.instructions[i]
@@ -80,9 +79,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun removeValuesClashingWithVariables( private fun removeValuesClashingWithVariables(
values: RedundantBoxedValuesCollection, values: RedundantBoxedValuesCollection,
node: MethodNode, node: MethodNode,
frames: Array<Frame<BasicValue>> frames: Array<Frame<BasicValue>>
) { ) {
while (removeValuesClashingWithVariablesPass(values, node, frames)) { while (removeValuesClashingWithVariablesPass(values, node, frames)) {
// do nothing // do nothing
@@ -90,9 +89,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun removeValuesClashingWithVariablesPass( private fun removeValuesClashingWithVariablesPass(
values: RedundantBoxedValuesCollection, values: RedundantBoxedValuesCollection,
node: MethodNode, node: MethodNode,
frames: Array<out Frame<BasicValue>?> frames: Array<out Frame<BasicValue>?>
): Boolean { ): Boolean {
var needToRepeat = false var needToRepeat = false
@@ -123,13 +122,13 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean = private fun isUnsafeToRemoveBoxingForConnectedValues(usedValues: List<BasicValue>, unboxedType: Type): Boolean =
usedValues.any { input -> usedValues.any { input ->
if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false if (input === StrictBasicValue.UNINITIALIZED_VALUE) return@any false
if (input !is BoxedBasicValue) return@any true if (input !is BoxedBasicValue) return@any true
val descriptor = input.descriptor val descriptor = input.descriptor
!descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType !descriptor.isSafeToRemove || descriptor.unboxedType != unboxedType
} }
private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>>) { private fun adaptLocalVariableTableForBoxedValues(node: MethodNode, frames: Array<Frame<BasicValue>>) {
for (localVariableNode in node.localVariables) { for (localVariableNode in node.localVariables) {
@@ -148,9 +147,9 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun getValuesStoredOrLoadedToVariable( private fun getValuesStoredOrLoadedToVariable(
localVariableNode: LocalVariableNode, localVariableNode: LocalVariableNode,
node: MethodNode, node: MethodNode,
frames: Array<out Frame<BasicValue>?> frames: Array<out Frame<BasicValue>?>
): List<BasicValue> { ): List<BasicValue> {
val values = ArrayList<BasicValue>() val values = ArrayList<BasicValue>()
val insnList = node.instructions val insnList = node.instructions
@@ -171,8 +170,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
(insn as VarInsnNode).`var` == localVariableNode.index) { (insn as VarInsnNode).`var` == localVariableNode.index) {
if (insn.getOpcode() == Opcodes.ASTORE) { if (insn.getOpcode() == Opcodes.ASTORE) {
values.add(frame.top()!!) values.add(frame.top()!!)
} } else {
else {
values.add(frame.getLocal(insn.`var`)) values.add(frame.getLocal(insn.`var`))
} }
} }
@@ -205,8 +203,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun adaptInstructionsForBoxedValues( private fun adaptInstructionsForBoxedValues(
node: MethodNode, node: MethodNode,
values: RedundantBoxedValuesCollection values: RedundantBoxedValuesCollection
) { ) {
for (value in values) { for (value in values) {
adaptInstructionsForBoxedValue(node, value) adaptInstructionsForBoxedValue(node, value)
@@ -228,8 +226,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
private fun adaptBoxingInstruction(node: MethodNode, value: BoxedValueDescriptor) { private fun adaptBoxingInstruction(node: MethodNode, value: BoxedValueDescriptor) {
if (!value.isFromProgressionIterator()) { if (!value.isFromProgressionIterator()) {
node.instructions.remove(value.boxingInsn) node.instructions.remove(value.boxingInsn)
} } else {
else {
val iterator = value.progressionIterator ?: error("iterator should not be null because isFromProgressionIterator returns true") val iterator = value.progressionIterator ?: error("iterator should not be null because isFromProgressionIterator returns true")
//add checkcast to kotlin/<T>Iterator before next() call //add checkcast to kotlin/<T>Iterator before next() call
@@ -237,20 +234,20 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
//invoke concrete method (kotlin/<T>iterator.next<T>()) //invoke concrete method (kotlin/<T>iterator.next<T>())
node.instructions.set( node.instructions.set(
value.boxingInsn, value.boxingInsn,
MethodInsnNode( MethodInsnNode(
Opcodes.INVOKEVIRTUAL, Opcodes.INVOKEVIRTUAL,
iterator.type.internalName, iterator.nextMethodName, iterator.nextMethodDesc, iterator.type.internalName, iterator.nextMethodName, iterator.nextMethodDesc,
false false
) )
) )
} }
} }
private fun adaptCastInstruction( private fun adaptCastInstruction(
node: MethodNode, node: MethodNode,
value: BoxedValueDescriptor, value: BoxedValueDescriptor,
castWithType: Pair<AbstractInsnNode, Type> castWithType: Pair<AbstractInsnNode, Type>
) { ) {
val castInsn = castWithType.getFirst() val castInsn = castWithType.getFirst()
val castInsnsListener = MethodNode(Opcodes.ASM5) val castInsnsListener = MethodNode(Opcodes.ASM5)
@@ -264,7 +261,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun adaptInstruction( private fun adaptInstruction(
node: MethodNode, insn: AbstractInsnNode, value: BoxedValueDescriptor node: MethodNode, insn: AbstractInsnNode, value: BoxedValueDescriptor
) { ) {
val isDoubleSize = value.isDoubleSize() val isDoubleSize = value.isDoubleSize()
@@ -286,8 +283,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
Opcodes.INSTANCEOF -> { Opcodes.INSTANCEOF -> {
node.instructions.insertBefore( node.instructions.insertBefore(
insn, insn,
InsnNode(if (isDoubleSize) Opcodes.POP2 else Opcodes.POP) InsnNode(if (isDoubleSize) Opcodes.POP2 else Opcodes.POP)
) )
node.instructions.set(insn, InsnNode(Opcodes.ICONST_1)) node.instructions.set(insn, InsnNode(Opcodes.ICONST_1))
} }
@@ -299,7 +296,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
insn.isJavaLangComparableCompareTo() -> insn.isJavaLangComparableCompareTo() ->
adaptJavaLangComparableCompareTo(node, insn, value) adaptJavaLangComparableCompareTo(node, insn, value)
insn.isJavaLangClassBoxing() || insn.isJavaLangClassBoxing() ||
insn.isJavaLangClassUnboxing() -> insn.isJavaLangClassUnboxing() ->
node.instructions.remove(insn) node.instructions.remove(insn)
else -> else ->
throwCannotAdaptInstruction(insn) throwCannotAdaptInstruction(insn)
@@ -309,8 +306,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
Opcodes.INVOKEINTERFACE -> { Opcodes.INVOKEINTERFACE -> {
if (insn.isJavaLangComparableCompareTo()) { if (insn.isJavaLangComparableCompareTo()) {
adaptJavaLangComparableCompareTo(node, insn, value) adaptJavaLangComparableCompareTo(node, insn, value)
} } else {
else {
throwCannotAdaptInstruction(insn) throwCannotAdaptInstruction(insn)
} }
} }
@@ -325,12 +321,12 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing = private fun throwCannotAdaptInstruction(insn: AbstractInsnNode): Nothing =
throw AssertionError("Cannot adapt instruction: ${insn.insnText}") throw AssertionError("Cannot adapt instruction: ${insn.insnText}")
private fun adaptAreEqualIntrinsic( private fun adaptAreEqualIntrinsic(
node: MethodNode, node: MethodNode,
insn: AbstractInsnNode, insn: AbstractInsnNode,
value: BoxedValueDescriptor value: BoxedValueDescriptor
) { ) {
val unboxedType = value.unboxedType val unboxedType = value.unboxedType
@@ -339,8 +335,8 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
adaptAreEqualIntrinsicForInt(node, insn) adaptAreEqualIntrinsicForInt(node, insn)
Type.LONG -> Type.LONG ->
adaptAreEqualIntrinsicForLong(node, insn) adaptAreEqualIntrinsicForLong(node, insn)
Type.OBJECT -> Type.OBJECT -> {
{} }
else -> else ->
throw AssertionError("Unexpected unboxed type kind: $unboxedType") throw AssertionError("Unexpected unboxed type kind: $unboxedType")
} }
@@ -353,8 +349,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
fuseAreEqualWithBranch(node, insn, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ) fuseAreEqualWithBranch(node, insn, Opcodes.IF_ICMPNE, Opcodes.IF_ICMPEQ)
remove(insn) remove(insn)
remove(next) remove(next)
} } else {
else {
ifEqual1Else0(node, insn, Opcodes.IF_ICMPNE) ifEqual1Else0(node, insn, Opcodes.IF_ICMPNE)
remove(insn) remove(insn)
} }
@@ -369,8 +364,7 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
fuseAreEqualWithBranch(node, insn, Opcodes.IFNE, Opcodes.IFEQ) fuseAreEqualWithBranch(node, insn, Opcodes.IFNE, Opcodes.IFEQ)
remove(insn) remove(insn)
remove(next) remove(next)
} } else {
else {
ifEqual1Else0(node, insn, Opcodes.IFNE) ifEqual1Else0(node, insn, Opcodes.IFNE)
remove(insn) remove(insn)
} }
@@ -378,10 +372,10 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun fuseAreEqualWithBranch( private fun fuseAreEqualWithBranch(
node: MethodNode, node: MethodNode,
insn: AbstractInsnNode, insn: AbstractInsnNode,
ifEqualOpcode: Int, ifEqualOpcode: Int,
ifNotEqualOpcode: Int ifNotEqualOpcode: Int
) { ) {
node.instructions.run { node.instructions.run {
val next = insn.next val next = insn.next
@@ -412,22 +406,22 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
} }
private fun adaptJavaLangComparableCompareTo( private fun adaptJavaLangComparableCompareTo(
node: MethodNode, node: MethodNode,
insn: AbstractInsnNode, insn: AbstractInsnNode,
value: BoxedValueDescriptor value: BoxedValueDescriptor
) { ) {
val unboxedType = value.unboxedType val unboxedType = value.unboxedType
when (unboxedType.sort) { when (unboxedType.sort) {
Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.INT, Type.CHAR -> Type.BOOLEAN, Type.BYTE, Type.SHORT, Type.INT, Type.CHAR ->
adaptJavaLangComparableCompareToForInt(node, insn) adaptJavaLangComparableCompareToForInt(node, insn)
Type.LONG -> Type.LONG ->
adaptJavaLangComparableCompareToForLong(node, insn) adaptJavaLangComparableCompareToForLong(node, insn)
Type.FLOAT -> Type.FLOAT ->
adaptJavaLangComparableCompareToForFloat(node, insn) adaptJavaLangComparableCompareToForFloat(node, insn)
Type.DOUBLE -> Type.DOUBLE ->
adaptJavaLangComparableCompareToForDouble(node, insn) adaptJavaLangComparableCompareToForDouble(node, insn)
else -> else ->
throw AssertionError("Unexpected unboxed type kind: $unboxedType") throw AssertionError("Unexpected unboxed type kind: $unboxedType")
} }
} }
@@ -438,15 +432,15 @@ class RedundantBoxingMethodTransformer : MethodTransformer() {
val next2 = next?.next val next2 = next?.next
when { when {
next != null && next2 != null && next != null && next2 != null &&
next.opcode == Opcodes.ICONST_0 && next.opcode == Opcodes.ICONST_0 &&
next2.opcode >= Opcodes.IF_ICMPEQ && next2.opcode <= Opcodes.IF_ICMPLE -> { next2.opcode >= Opcodes.IF_ICMPEQ && next2.opcode <= Opcodes.IF_ICMPLE -> {
// Fuse: compareTo + ICONST_0 + IF_ICMPxx -> IF_ICMPxx // Fuse: compareTo + ICONST_0 + IF_ICMPxx -> IF_ICMPxx
remove(insn) remove(insn)
remove(next) remove(next)
} }
next != null && next != null &&
next.opcode >= Opcodes.IFEQ && next.opcode <= Opcodes.IFLE -> { next.opcode >= Opcodes.IFEQ && next.opcode <= Opcodes.IFLE -> {
// Fuse: compareTo + IFxx -> IF_ICMPxx // Fuse: compareTo + IFxx -> IF_ICMPxx
val nextLabel = (next as JumpInsnNode).label val nextLabel = (next as JumpInsnNode).label
val ifCmpOpcode = next.opcode - Opcodes.IFEQ + Opcodes.IF_ICMPEQ val ifCmpOpcode = next.opcode - Opcodes.IFEQ + Opcodes.IF_ICMPEQ
@@ -84,8 +84,7 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
it.set(insn, InsnNode(Opcodes.NOP)) it.set(insn, InsnNode(Opcodes.NOP))
it.remove(prevNonNop) it.remove(prevNonNop)
} }
} } else if (i > 1) {
else if (i > 1) {
val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn val prevNonNop2 = prevNonNop.findPreviousOrNull { it.opcode != Opcodes.NOP } ?: continue@forInsn
if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) { if (prevNonNop.isEliminatedByPop() && prevNonNop2.isEliminatedByPop()) {
actions.add { actions.add {
@@ -112,24 +111,24 @@ class StackPeepholeOptimizationsTransformer : MethodTransformer() {
} }
private fun AbstractInsnNode.isEliminatedByPop() = private fun AbstractInsnNode.isEliminatedByPop() =
isPurePushOfSize1() || isPurePushOfSize1() ||
opcode == Opcodes.DUP opcode == Opcodes.DUP
private fun AbstractInsnNode.isPurePushOfSize1(): Boolean = private fun AbstractInsnNode.isPurePushOfSize1(): Boolean =
opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 || opcode in Opcodes.ACONST_NULL..Opcodes.FCONST_2 ||
opcode in Opcodes.BIPUSH..Opcodes.ILOAD || opcode in Opcodes.BIPUSH..Opcodes.ILOAD ||
opcode == Opcodes.FLOAD || opcode == Opcodes.FLOAD ||
opcode == Opcodes.ALOAD || opcode == Opcodes.ALOAD ||
isUnitInstance() isUnitInstance()
private fun AbstractInsnNode.isEliminatedByPop2() = private fun AbstractInsnNode.isEliminatedByPop2() =
isPurePushOfSize2() || isPurePushOfSize2() ||
opcode == Opcodes.DUP2 opcode == Opcodes.DUP2
private fun AbstractInsnNode.isPurePushOfSize2(): Boolean = private fun AbstractInsnNode.isPurePushOfSize2(): Boolean =
opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 || opcode == Opcodes.LCONST_0 || opcode == Opcodes.LCONST_1 ||
opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 || opcode == Opcodes.DCONST_0 || opcode == Opcodes.DCONST_1 ||
opcode == Opcodes.LLOAD || opcode == Opcodes.LLOAD ||
opcode == Opcodes.DLOAD opcode == Opcodes.DLOAD
} }
@@ -22,8 +22,8 @@ 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
class CustomFramesMethodAnalyzer<V : Value>( class CustomFramesMethodAnalyzer<V : Value>(
owner: String, method: MethodNode, interpreter: Interpreter<V>, owner: String, method: MethodNode, interpreter: Interpreter<V>,
private val frameFactory: (Int, Int) -> Frame<V> private val frameFactory: (Int, Int) -> Frame<V>
) : MethodAnalyzer<V>(owner, method, interpreter) { ) : MethodAnalyzer<V>(owner, method, interpreter) {
override fun newFrame(nLocals: Int, nStack: Int) = frameFactory(nLocals, nStack) override fun newFrame(nLocals: Int, nStack: Int) = frameFactory(nLocals, nStack)
} }
@@ -62,9 +62,9 @@ import java.util.*
* @author Dmitry Petrov * @author Dmitry Petrov
*/ */
open class MethodAnalyzer<V : Value>( open class MethodAnalyzer<V : Value>(
val owner: String, val owner: String,
val method: MethodNode, val method: MethodNode,
protected val interpreter: Interpreter<V> protected val interpreter: Interpreter<V>
) { ) {
val instructions: InsnList = method.instructions val instructions: InsnList = method.instructions
private val nInsns: Int = instructions.size() private val nInsns: Int = instructions.size()
@@ -91,7 +91,7 @@ open class MethodAnalyzer<V : Value>(
protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true protected open fun visitControlFlowExceptionEdge(insn: Int, successor: Int): Boolean = true
protected open fun visitControlFlowExceptionEdge(insn: Int, tcb: TryCatchBlockNode): Boolean = protected open fun visitControlFlowExceptionEdge(insn: Int, tcb: TryCatchBlockNode): Boolean =
visitControlFlowExceptionEdge(insn, instructions.indexOf(tcb.handler)) visitControlFlowExceptionEdge(insn, instructions.indexOf(tcb.handler))
fun analyze(): Array<Frame<V>?> { fun analyze(): Array<Frame<V>?> {
if (nInsns == 0) return frames if (nInsns == 0) return frames
@@ -116,8 +116,7 @@ open class MethodAnalyzer<V : Value>(
if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) { if (insnType == AbstractInsnNode.LABEL || insnType == AbstractInsnNode.LINE || insnType == AbstractInsnNode.FRAME) {
visitNopInsn(f, insn) visitNopInsn(f, insn)
} } else {
else {
current.init(f).execute(insnNode, interpreter) current.init(f).execute(insnNode, interpreter)
when { when {
@@ -129,12 +128,13 @@ open class MethodAnalyzer<V : Value>(
visitTableSwitchInsnNode(insnNode, current, insn) visitTableSwitchInsnNode(insnNode, current, insn)
insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) -> insnOpcode != Opcodes.ATHROW && (insnOpcode < Opcodes.IRETURN || insnOpcode > Opcodes.RETURN) ->
visitOpInsn(current, insn) visitOpInsn(current, insn)
else -> {} else -> {
}
} }
} }
handlers[insn]?.forEach { tcb -> handlers[insn]?.forEach { tcb ->
val exnType = Type.getObjectType(tcb.type?:"java/lang/Throwable") val exnType = Type.getObjectType(tcb.type ?: "java/lang/Throwable")
val jump = instructions.indexOf(tcb.handler) val jump = instructions.indexOf(tcb.handler)
if (visitControlFlowExceptionEdge(insn, tcb)) { if (visitControlFlowExceptionEdge(insn, tcb)) {
handler.init(f) handler.init(f)
@@ -144,11 +144,9 @@ open class MethodAnalyzer<V : Value>(
} }
} }
} } catch (e: AnalyzerException) {
catch (e: AnalyzerException) {
throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e) throw AnalyzerException(e.node, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
} } catch (e: Exception) {
catch (e: Exception) {
throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e) throw AnalyzerException(insnNode, "Error at instruction #$insn ${insnNode.insnText}: ${e.message}", e)
} }
@@ -158,7 +156,7 @@ open class MethodAnalyzer<V : Value>(
} }
fun getFrame(insn: AbstractInsnNode): Frame<V>? = fun getFrame(insn: AbstractInsnNode): Frame<V>? =
frames[instructions.indexOf(insn)] frames[instructions.indexOf(insn)]
private fun checkAssertions() { private fun checkAssertions() {
if (instructions.toArray().any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET }) if (instructions.toArray().any { it.opcode == Opcodes.JSR || it.opcode == Opcodes.RET })
@@ -250,12 +248,12 @@ open class MethodAnalyzer<V : Value>(
private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) { private fun mergeControlFlowEdge(insn: Int, frame: Frame<V>) {
val oldFrame = frames[insn] val oldFrame = frames[insn]
val changes = val changes =
if (oldFrame != null) if (oldFrame != null)
oldFrame.merge(frame, interpreter) oldFrame.merge(frame, interpreter)
else { else {
frames[insn] = newFrame(frame) frames[insn] = newFrame(frame)
true true
} }
if (changes && !queued[insn]) { if (changes && !queued[insn]) {
queued[insn] = true queued[insn] = true
queue[top++] = insn queue[top++] = insn
@@ -23,61 +23,60 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() { abstract class ReferenceTrackingInterpreter : OptimizationBasicInterpreter() {
override fun merge(v: BasicValue, w: BasicValue): BasicValue = override fun merge(v: BasicValue, w: BasicValue): BasicValue =
when { when {
v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue -> v is ProperTrackedReferenceValue && w is ProperTrackedReferenceValue ->
if (v.descriptor == w.descriptor) if (v.descriptor == w.descriptor)
v v
else else
createTaintedValue(v, w)
v is TrackedReferenceValue && w is TrackedReferenceValue ->
createPossiblyMergedValue(v, w)
v is TrackedReferenceValue || w is TrackedReferenceValue ->
createTaintedValue(v, w) createTaintedValue(v, w)
else -> v is TrackedReferenceValue && w is TrackedReferenceValue ->
super.merge(v, w) createPossiblyMergedValue(v, w)
}
protected fun createTaintedValue(v: BasicValue, w: BasicValue) : TrackedReferenceValue = v is TrackedReferenceValue || w is TrackedReferenceValue ->
TaintedTrackedReferenceValue( createTaintedValue(v, w)
getMergedValueType(v.type, w.type),
mergeDescriptors(v, w).also { else ->
assert(it.isNotEmpty()) { "At least one of ($v, $w) should be a tracked reference" } super.merge(v, w)
} }
)
protected fun createTaintedValue(v: BasicValue, w: BasicValue): TrackedReferenceValue =
TaintedTrackedReferenceValue(
getMergedValueType(v.type, w.type),
mergeDescriptors(v, w).also {
assert(it.isNotEmpty()) { "At least one of ($v, $w) should be a tracked reference" }
}
)
protected fun createMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue = protected fun createMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
if (v is TaintedTrackedReferenceValue || w is TaintedTrackedReferenceValue) if (v is TaintedTrackedReferenceValue || w is TaintedTrackedReferenceValue)
createTaintedValue(v, w) createTaintedValue(v, w)
else else
MergedTrackedReferenceValue(getMergedValueType(v.type, w.type), mergeDescriptors(v, w)) MergedTrackedReferenceValue(getMergedValueType(v.type, w.type), mergeDescriptors(v, w))
protected open fun createPossiblyMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue = protected open fun createPossiblyMergedValue(v: TrackedReferenceValue, w: TrackedReferenceValue): TrackedReferenceValue =
createTaintedValue(v, w) createTaintedValue(v, w)
private fun mergeDescriptors(v: BasicValue, w: BasicValue) = private fun mergeDescriptors(v: BasicValue, w: BasicValue) =
v.referenceValueDescriptors + w.referenceValueDescriptors v.referenceValueDescriptors + w.referenceValueDescriptors
private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor> private val BasicValue.referenceValueDescriptors: Set<ReferenceValueDescriptor>
get() = if (this is TrackedReferenceValue) this.descriptors else emptySet() get() = if (this is TrackedReferenceValue) this.descriptors else emptySet()
protected fun getMergedValueType(type1: Type?, type2: Type?): Type = protected fun getMergedValueType(type1: Type?, type2: Type?): Type =
when { when {
type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE type1 == null || type2 == null -> AsmTypes.OBJECT_TYPE
type1 == type2 -> type1 type1 == type2 -> type1
else -> AsmTypes.OBJECT_TYPE else -> AsmTypes.OBJECT_TYPE
} }
override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? = override fun copyOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? =
if (value is TrackedReferenceValue) { if (value is TrackedReferenceValue) {
checkRefValuesUsages(insn, listOf(value)) checkRefValuesUsages(insn, listOf(value))
value value
} } else {
else { super.copyOperation(insn, value)
super.copyOperation(insn, value) }
}
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? { override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue): BasicValue? {
checkRefValuesUsages(insn, listOf(value)) checkRefValuesUsages(insn, listOf(value))
@@ -22,7 +22,7 @@ interface ReferenceValueDescriptor {
fun onUseAsTainted() fun onUseAsTainted()
} }
sealed class TrackedReferenceValue(type: Type): StrictBasicValue(type) { sealed class TrackedReferenceValue(type: Type) : StrictBasicValue(type) {
abstract val descriptors: Set<ReferenceValueDescriptor> abstract val descriptors: Set<ReferenceValueDescriptor>
} }
@@ -31,37 +31,38 @@ class ProperTrackedReferenceValue(type: Type, val descriptor: ReferenceValueDesc
get() = setOf(descriptor) get() = setOf(descriptor)
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other === this || other === this ||
other is ProperTrackedReferenceValue && other.descriptor == this.descriptor other is ProperTrackedReferenceValue && other.descriptor == this.descriptor
override fun hashCode(): Int = override fun hashCode(): Int =
descriptor.hashCode() descriptor.hashCode()
override fun toString(): String = override fun toString(): String =
"[$descriptor]" "[$descriptor]"
} }
class MergedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) { class MergedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other === this || other === this ||
other is MergedTrackedReferenceValue && other.descriptors == this.descriptors other is MergedTrackedReferenceValue && other.descriptors == this.descriptors
override fun hashCode(): Int = override fun hashCode(): Int =
descriptors.hashCode() descriptors.hashCode()
override fun toString(): String = override fun toString(): String =
descriptors.toString() descriptors.toString()
} }
class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) { class TaintedTrackedReferenceValue(type: Type, override val descriptors: Set<ReferenceValueDescriptor>) : TrackedReferenceValue(type) {
override fun equals(other: Any?): Boolean = override fun equals(other: Any?): Boolean =
other === this || other === this ||
other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors other is TaintedTrackedReferenceValue && other.descriptors == this.descriptors
override fun hashCode(): Int = override fun hashCode(): Int =
descriptors.hashCode() descriptors.hashCode()
override fun toString(): String = override fun toString(): String =
"!$descriptors" "!$descriptors"
} }
@@ -27,11 +27,12 @@ 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.*
val AbstractInsnNode.isMeaningful: Boolean get() = val AbstractInsnNode.isMeaningful: Boolean
when (this.type) { get() =
AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false when (this.type) {
else -> true AbstractInsnNode.LABEL, AbstractInsnNode.LINE, AbstractInsnNode.FRAME -> false
} else -> true
}
class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequence<AbstractInsnNode> { class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequence<AbstractInsnNode> {
constructor(insnList: InsnList) : this(insnList.first, null) constructor(insnList: InsnList) : this(insnList.first, null)
@@ -44,6 +45,7 @@ class InsnSequence(val from: AbstractInsnNode, val to: AbstractInsnNode?) : Sequ
current = current!!.next current = current!!.next
return result!! return result!!
} }
override fun hasNext() = current != to override fun hasNext() = current != to
} }
} }
@@ -74,13 +76,15 @@ fun MethodNode.prepareForEmitting() {
current = prev current = prev
} }
maxStack = -1 maxStack = -1
accept(MaxStackFrameSizeAndLocalsCalculator( accept(
MaxStackFrameSizeAndLocalsCalculator(
Opcodes.ASM5, access, desc, Opcodes.ASM5, access, desc,
object : MethodVisitor(Opcodes.ASM5) { object : MethodVisitor(Opcodes.ASM5) {
override fun visitMaxs(maxStack: Int, maxLocals: Int) { override fun visitMaxs(maxStack: Int, maxLocals: Int) {
this@prepareForEmitting.maxStack = maxStack this@prepareForEmitting.maxStack = maxStack
} }
})) })
)
} }
fun MethodNode.stripOptimizationMarkers() { fun MethodNode.stripOptimizationMarkers() {
@@ -88,15 +92,14 @@ fun MethodNode.stripOptimizationMarkers() {
while (insn != null) { while (insn != null) {
if (isOptimizationMarker(insn)) { if (isOptimizationMarker(insn)) {
insn = instructions.removeNodeGetNext(insn) insn = instructions.removeNodeGetNext(insn)
} } else {
else {
insn = insn.next insn = insn.next
} }
} }
} }
private fun isOptimizationMarker(insn: AbstractInsnNode) = private fun isOptimizationMarker(insn: AbstractInsnNode) =
PseudoInsn.STORE_NOT_NULL.isa(insn) PseudoInsn.STORE_NOT_NULL.isa(insn)
fun MethodNode.removeEmptyCatchBlocks() { fun MethodNode.removeEmptyCatchBlocks() {
tryCatchBlocks = tryCatchBlocks.filter { tcb -> tryCatchBlocks = tryCatchBlocks.filter { tcb ->
@@ -143,7 +146,7 @@ fun MethodNode.removeUnusedLocalVariables() {
} }
private fun VarInsnNode.isSize2LoadStoreOperation() = private fun VarInsnNode.isSize2LoadStoreOperation() =
opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE opcode == LLOAD || opcode == DLOAD || opcode == LSTORE || opcode == DSTORE
fun MethodNode.remapLocalVariables(remapping: IntArray) { fun MethodNode.remapLocalVariables(remapping: IntArray) {
for (insn in instructions.toArray()) { for (insn in instructions.toArray()) {
@@ -177,7 +180,7 @@ inline fun AbstractInsnNode.findPreviousOrNull(predicate: (AbstractInsnNode) ->
} }
fun AbstractInsnNode.hasOpcode(): Boolean = fun AbstractInsnNode.hasOpcode(): Boolean =
opcode >= 0 opcode >= 0
// See InstructionAdapter // See InstructionAdapter
// //
@@ -192,27 +195,29 @@ fun AbstractInsnNode.hasOpcode(): Boolean =
// mv.visitLdcInsn(new Integer(cst)); // mv.visitLdcInsn(new Integer(cst));
// } // }
// } // }
val AbstractInsnNode.intConstant: Int? get() = val AbstractInsnNode.intConstant: Int?
when (opcode) { get() =
in ICONST_M1..ICONST_5 -> opcode - ICONST_0 when (opcode) {
BIPUSH, SIPUSH -> (this as IntInsnNode).operand in ICONST_M1..ICONST_5 -> opcode - ICONST_0
LDC -> (this as LdcInsnNode).cst as? Int BIPUSH, SIPUSH -> (this as IntInsnNode).operand
else -> null LDC -> (this as LdcInsnNode).cst as? Int
} else -> null
}
fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } } fun insnListOf(vararg insns: AbstractInsnNode) = InsnList().apply { insns.forEach { add(it) } }
fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE fun AbstractInsnNode.isStoreOperation(): Boolean = opcode in Opcodes.ISTORE..Opcodes.ASTORE
fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD fun AbstractInsnNode.isLoadOperation(): Boolean = opcode in Opcodes.ILOAD..Opcodes.ALOAD
val AbstractInsnNode?.debugText get() = val AbstractInsnNode?.debugText
get() =
if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText" if (this == null) "<null>" else "${this::class.java.simpleName}: $insnText"
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean = internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.isInsn(opcode: Int, condition: T.() -> Boolean): Boolean =
takeInsnIf(opcode, condition) != null takeInsnIf(opcode, condition) != null
internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? = internal inline fun <reified T : AbstractInsnNode> AbstractInsnNode.takeInsnIf(opcode: Int, condition: T.() -> Boolean): T? =
takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() } takeIf { it.opcode == opcode }?.safeAs<T>()?.takeIf { it.condition() }
fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) { fun InsnList.removeAll(nodes: Collection<AbstractInsnNode>) {
for (node in nodes) remove(node) for (node in nodes) remove(node)
@@ -51,13 +51,14 @@ class VariableLivenessFrame(val maxLocals: Int) : VarFrame<VariableLivenessFrame
override fun hashCode() = bitSet.hashCode() override fun hashCode() = bitSet.hashCode()
} }
fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> { fun analyzeLiveness(node: MethodNode): List<VariableLivenessFrame> {
val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter()) val typeAnnotatedFrames = MethodTransformer.analyze("fake", node, OptimizationBasicInterpreter())
return analyze(node, object : BackwardAnalysisInterpreter<VariableLivenessFrame> { return analyze(node, object : BackwardAnalysisInterpreter<VariableLivenessFrame> {
override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals) override fun newFrame(maxLocals: Int) = VariableLivenessFrame(maxLocals)
override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn) override fun def(frame: VariableLivenessFrame, insn: AbstractInsnNode) = defVar(frame, insn)
override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) = override fun use(frame: VariableLivenessFrame, insn: AbstractInsnNode) =
useVar(frame, insn, node, typeAnnotatedFrames[node.instructions.indexOf(insn)]) useVar(frame, insn, node, typeAnnotatedFrames[node.instructions.indexOf(insn)])
}) })
} }
@@ -69,24 +70,23 @@ private fun defVar(frame: VariableLivenessFrame, insn: AbstractInsnNode) {
} }
private fun useVar( private fun useVar(
frame: VariableLivenessFrame, frame: VariableLivenessFrame,
insn: AbstractInsnNode, insn: AbstractInsnNode,
node: MethodNode, node: MethodNode,
// May be null in case of dead code // May be null in case of dead code
typeAnnotatedFrame: Frame<BasicValue>? typeAnnotatedFrame: Frame<BasicValue>?
) { ) {
val index = node.instructions.indexOf(insn) val index = node.instructions.indexOf(insn)
node.localVariables.filter { node.localVariables.filter {
node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) && node.instructions.indexOf(it.start) < index && index < node.instructions.indexOf(it.end) &&
Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort Type.getType(it.desc).sort == typeAnnotatedFrame?.getLocal(it.index)?.type?.sort
}.forEach { }.forEach {
frame.markAlive(it.index) frame.markAlive(it.index)
} }
if (insn is VarInsnNode && insn.isLoadOperation()) { if (insn is VarInsnNode && insn.isLoadOperation()) {
frame.markAlive(insn.`var`) frame.markAlive(insn.`var`)
} } else if (insn is IincInsnNode) {
else if (insn is IincInsnNode) {
frame.markAlive(insn.`var`) frame.markAlive(insn.`var`)
} }
} }
@@ -30,15 +30,15 @@ import java.util.*
private class DecompiledTryDescriptor(val tryStartLabel: LabelNode) { private class DecompiledTryDescriptor(val tryStartLabel: LabelNode) {
// Only used for assertions // Only used for assertions
var defaultHandlerTcb : TryCatchBlockNode? = null var defaultHandlerTcb: TryCatchBlockNode? = null
val handlerStartLabels = hashSetOf<LabelNode>() val handlerStartLabels = hashSetOf<LabelNode>()
} }
private fun TryCatchBlockNode.isDefaultHandlerNode(): Boolean = private fun TryCatchBlockNode.isDefaultHandlerNode(): Boolean =
start == handler start == handler
private fun MethodNode.debugString(tcb: TryCatchBlockNode): String = private fun MethodNode.debugString(tcb: TryCatchBlockNode): String =
"TCB<${instructions.indexOf(tcb.start)}, ${instructions.indexOf(tcb.end)}, ${instructions.indexOf(tcb.handler)}>" "TCB<${instructions.indexOf(tcb.start)}, ${instructions.indexOf(tcb.end)}, ${instructions.indexOf(tcb.handler)}>"
internal fun insertTryCatchBlocksMarkers(methodNode: MethodNode): Map<AbstractInsnNode, AbstractInsnNode> { internal fun insertTryCatchBlocksMarkers(methodNode: MethodNode): Map<AbstractInsnNode, AbstractInsnNode> {
if (methodNode.tryCatchBlocks.isEmpty()) return emptyMap() if (methodNode.tryCatchBlocks.isEmpty()) return emptyMap()
@@ -64,9 +64,9 @@ private fun transformTryCatchBlocks(methodNode: MethodNode, newTryStartLabels: H
} }
private fun insertSaveRestoreStackMarkers( private fun insertSaveRestoreStackMarkers(
decompiledTryDescriptorForStart: Map<LabelNode, DecompiledTryDescriptor>, decompiledTryDescriptorForStart: Map<LabelNode, DecompiledTryDescriptor>,
methodNode: MethodNode, methodNode: MethodNode,
newTryStartLabels: MutableMap<LabelNode, LabelNode> newTryStartLabels: MutableMap<LabelNode, LabelNode>
): Map<AbstractInsnNode, AbstractInsnNode> { ): Map<AbstractInsnNode, AbstractInsnNode> {
val restoreStackToSaveMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>() val restoreStackToSaveMarker = hashMapOf<AbstractInsnNode, AbstractInsnNode>()
val saveStackMarkerByTryLabel = hashMapOf<LabelNode, AbstractInsnNode>() val saveStackMarkerByTryLabel = hashMapOf<LabelNode, AbstractInsnNode>()
@@ -92,8 +92,7 @@ private fun insertSaveRestoreStackMarkers(
methodNode.instructions.insertBefore(nopNode, saveStackMarker) methodNode.instructions.insertBefore(nopNode, saveStackMarker)
methodNode.instructions.insertBefore(nopNode, newTryStartLabel) methodNode.instructions.insertBefore(nopNode, newTryStartLabel)
methodNode.instructions.insert(nopNode, restoreStackMarker) methodNode.instructions.insert(nopNode, restoreStackMarker)
} } else {
else {
saveStackMarker = saveStackMarkerByTryLabel[tryStartLabel]!! saveStackMarker = saveStackMarkerByTryLabel[tryStartLabel]!!
} }
@@ -24,16 +24,19 @@ import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer
import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
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.AbstractInsnNode
import org.jetbrains.org.objectweb.asm.tree.JumpInsnNode
import org.jetbrains.org.objectweb.asm.tree.LabelNode
import org.jetbrains.org.objectweb.asm.tree.MethodNode
import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
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
internal class FixStackAnalyzer( internal class FixStackAnalyzer(
owner: String, owner: String,
val method: MethodNode, val method: MethodNode,
val context: FixStackContext, val context: FixStackContext,
private val skipBreakContinueGotoEdges: Boolean = true private val skipBreakContinueGotoEdges: Boolean = true
) { ) {
companion object { companion object {
// Stack size is always non-negative // Stack size is always non-negative
@@ -79,7 +82,7 @@ internal class FixStackAnalyzer(
} }
override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> = override fun newFrame(nLocals: Int, nStack: Int): Frame<BasicValue> =
FixStackFrame(nLocals, nStack) FixStackFrame(nLocals, nStack)
private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node) private fun indexOf(node: AbstractInsnNode) = method.instructions.indexOf(node)
@@ -128,8 +131,7 @@ internal class FixStackAnalyzer(
override fun push(value: BasicValue) { override fun push(value: BasicValue) {
if (super.getStackSize() < maxStackSize) { if (super.getStackSize() < maxStackSize) {
super.push(value) super.push(value)
} } else {
else {
extraStack.add(value) extraStack.add(value)
maxExtraStackSize = Math.max(maxExtraStackSize, extraStack.size) maxExtraStackSize = Math.max(maxExtraStackSize, extraStack.size)
} }
@@ -142,8 +144,7 @@ internal class FixStackAnalyzer(
override fun pop(): BasicValue { override fun pop(): BasicValue {
return if (extraStack.isNotEmpty()) { return if (extraStack.isNotEmpty()) {
extraStack.pop() extraStack.pop()
} } else {
else {
super.pop() super.pop()
} }
} }
@@ -151,8 +152,7 @@ internal class FixStackAnalyzer(
override fun getStack(i: Int): BasicValue { override fun getStack(i: Int): BasicValue {
return if (i < super.getMaxStackSize()) { return if (i < super.getMaxStackSize()) {
super.getStack(i) super.getStack(i)
} } else {
else {
extraStack[i - maxStackSize] extraStack[i - maxStackSize]
} }
} }
@@ -176,8 +176,7 @@ internal class FixStackAnalyzer(
val savedValues = spilledStacks[beforeInlineMarker] val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!) pushAll(savedValues!!)
push(returnValue) push(returnValue)
} } else {
else {
val savedValues = spilledStacks[beforeInlineMarker] val savedValues = spilledStacks[beforeInlineMarker]
pushAll(savedValues!!) pushAll(savedValues!!)
} }
@@ -197,5 +196,4 @@ internal class FixStackAnalyzer(
} }
} }
@@ -91,15 +91,15 @@ internal class FixStackContext(val methodNode: MethodNode) {
private fun indexOf(node: AbstractInsnNode) = methodNode.instructions.indexOf(node) private fun indexOf(node: AbstractInsnNode) = methodNode.instructions.indexOf(node)
fun hasAnyMarkers(): Boolean = fun hasAnyMarkers(): Boolean =
breakContinueGotoNodes.isNotEmpty() || breakContinueGotoNodes.isNotEmpty() ||
fakeAlwaysTrueIfeqMarkers.isNotEmpty() || fakeAlwaysTrueIfeqMarkers.isNotEmpty() ||
fakeAlwaysFalseIfeqMarkers.isNotEmpty() || fakeAlwaysFalseIfeqMarkers.isNotEmpty() ||
isThereAnyTryCatch || isThereAnyTryCatch ||
openingInlineMethodMarker.isNotEmpty() openingInlineMethodMarker.isNotEmpty()
fun isAnalysisRequired(): Boolean = fun isAnalysisRequired(): Boolean =
breakContinueGotoNodes.isNotEmpty() || breakContinueGotoNodes.isNotEmpty() ||
isThereAnyTryCatch || isThereAnyTryCatch ||
openingInlineMethodMarker.isNotEmpty() openingInlineMethodMarker.isNotEmpty()
} }
@@ -89,10 +89,10 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformBreakContinueGotos( private fun transformBreakContinueGotos(
methodNode: MethodNode, methodNode: MethodNode,
fixStackContext: FixStackContext, fixStackContext: FixStackContext,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
analyzer: FixStackAnalyzer analyzer: FixStackAnalyzer
) { ) {
fixStackContext.breakContinueGotoNodes.forEach { gotoNode -> fixStackContext.breakContinueGotoNodes.forEach { gotoNode ->
val gotoIndex = methodNode.instructions.indexOf(gotoNode) val gotoIndex = methodNode.instructions.indexOf(gotoNode)
@@ -106,13 +106,11 @@ class FixStackMethodTransformer : MethodTransformer() {
"Label at $labelIndex, jump at $gotoIndex: stack underflow: $expectedStackSize > $actualStackSize" "Label at $labelIndex, jump at $gotoIndex: stack underflow: $expectedStackSize > $actualStackSize"
} }
val actualStackContent = analyzer.getActualStack(gotoNode) val actualStackContent = analyzer.getActualStack(gotoNode)
?: throw AssertionError("Jump at $gotoIndex should be alive") ?: throw AssertionError("Jump at $gotoIndex should be alive")
actions.add { replaceMarkerWithPops(methodNode, gotoNode.previous, expectedStackSize, actualStackContent) } actions.add { replaceMarkerWithPops(methodNode, gotoNode.previous, expectedStackSize, actualStackContent) }
} } else if (actualStackSize >= 0 && expectedStackSize < 0) {
else if (actualStackSize >= 0 && expectedStackSize < 0) {
throw AssertionError("Live jump $gotoIndex to dead label $labelIndex") throw AssertionError("Live jump $gotoIndex to dead label $labelIndex")
} } else {
else {
val marker = gotoNode.previous val marker = gotoNode.previous
actions.add { methodNode.instructions.remove(marker) } actions.add { methodNode.instructions.remove(marker) }
} }
@@ -120,10 +118,10 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformSaveRestoreStackMarkers( private fun transformSaveRestoreStackMarkers(
methodNode: MethodNode, methodNode: MethodNode,
context: FixStackContext, context: FixStackContext,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
analyzer: FixStackAnalyzer analyzer: FixStackAnalyzer
) { ) {
val localVariablesManager = LocalVariablesManager(context, methodNode) val localVariablesManager = LocalVariablesManager(context, methodNode)
InsnSequence(methodNode.instructions).forEach { marker -> InsnSequence(methodNode.instructions).forEach { marker ->
@@ -142,18 +140,17 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformSaveStackMarker( private fun transformSaveStackMarker(
methodNode: MethodNode, methodNode: MethodNode,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
analyzer: FixStackAnalyzer, analyzer: FixStackAnalyzer,
marker: AbstractInsnNode, marker: AbstractInsnNode,
localVariablesManager: LocalVariablesManager localVariablesManager: LocalVariablesManager
) { ) {
val savedStackValues = analyzer.getStackToSpill(marker) val savedStackValues = analyzer.getStackToSpill(marker)
if (savedStackValues != null) { if (savedStackValues != null) {
val savedStackDescriptor = localVariablesManager.allocateVariablesForSaveStackMarker(marker, savedStackValues) val savedStackDescriptor = localVariablesManager.allocateVariablesForSaveStackMarker(marker, savedStackValues)
actions.add { saveStack(methodNode, marker, savedStackDescriptor) } actions.add { saveStack(methodNode, marker, savedStackDescriptor) }
} } else {
else {
// marker is dead code // marker is dead code
localVariablesManager.allocateVariablesForSaveStackMarker(marker, emptyList()) localVariablesManager.allocateVariablesForSaveStackMarker(marker, emptyList())
actions.add { methodNode.instructions.remove(marker) } actions.add { methodNode.instructions.remove(marker) }
@@ -161,10 +158,10 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformRestoreStackMarker( private fun transformRestoreStackMarker(
methodNode: MethodNode, methodNode: MethodNode,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
marker: AbstractInsnNode, marker: AbstractInsnNode,
localVariablesManager: LocalVariablesManager localVariablesManager: LocalVariablesManager
) { ) {
val savedStackDescriptor = localVariablesManager.getSavedStackDescriptor(marker) val savedStackDescriptor = localVariablesManager.getSavedStackDescriptor(marker)
actions.add { restoreStack(methodNode, marker, savedStackDescriptor) } actions.add { restoreStack(methodNode, marker, savedStackDescriptor) }
@@ -172,11 +169,11 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformAfterInlineCallMarker( private fun transformAfterInlineCallMarker(
methodNode: MethodNode, methodNode: MethodNode,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
analyzer: FixStackAnalyzer, analyzer: FixStackAnalyzer,
inlineMarker: AbstractInsnNode, inlineMarker: AbstractInsnNode,
localVariablesManager: LocalVariablesManager localVariablesManager: LocalVariablesManager
) { ) {
val savedStackDescriptor = localVariablesManager.getBeforeInlineDescriptor(inlineMarker) val savedStackDescriptor = localVariablesManager.getBeforeInlineDescriptor(inlineMarker)
val stackContentAfterInline = analyzer.getActualStack(inlineMarker) val stackContentAfterInline = analyzer.getActualStack(inlineMarker)
@@ -186,8 +183,9 @@ class FixStackMethodTransformer : MethodTransformer() {
val returnValue = stackContentAfterInline.last() val returnValue = stackContentAfterInline.last()
val returnValueLocalVarIndex = localVariablesManager.createReturnValueVariable(returnValue) val returnValueLocalVarIndex = localVariablesManager.createReturnValueVariable(returnValue)
actions.add { actions.add {
restoreStackWithReturnValue(methodNode, inlineMarker, savedStackDescriptor, restoreStackWithReturnValue(
returnValue, returnValueLocalVarIndex methodNode, inlineMarker, savedStackDescriptor,
returnValue, returnValueLocalVarIndex
) )
} }
} }
@@ -196,8 +194,7 @@ class FixStackMethodTransformer : MethodTransformer() {
else -> else ->
throw AssertionError("Inline method should not leave more than 1 value on stack") throw AssertionError("Inline method should not leave more than 1 value on stack")
} }
} } else {
else {
// after inline marker is dead code // after inline marker is dead code
actions.add { methodNode.instructions.remove(inlineMarker) } actions.add { methodNode.instructions.remove(inlineMarker) }
} }
@@ -205,18 +202,17 @@ class FixStackMethodTransformer : MethodTransformer() {
} }
private fun transformBeforeInlineCallMarker( private fun transformBeforeInlineCallMarker(
methodNode: MethodNode, methodNode: MethodNode,
actions: MutableList<() -> Unit>, actions: MutableList<() -> Unit>,
analyzer: FixStackAnalyzer, analyzer: FixStackAnalyzer,
inlineMarker: AbstractInsnNode, inlineMarker: AbstractInsnNode,
localVariablesManager: LocalVariablesManager localVariablesManager: LocalVariablesManager
) { ) {
val savedStackValues = analyzer.getStackToSpill(inlineMarker) val savedStackValues = analyzer.getStackToSpill(inlineMarker)
if (savedStackValues != null) { if (savedStackValues != null) {
val savedStackDescriptor = localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, savedStackValues) val savedStackDescriptor = localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, savedStackValues)
actions.add { saveStack(methodNode, inlineMarker, savedStackDescriptor) } actions.add { saveStack(methodNode, inlineMarker, savedStackDescriptor) }
} } else {
else {
// before inline marker is dead code // before inline marker is dead code
localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, emptyList()) localVariablesManager.allocateVariablesForBeforeInlineMarker(inlineMarker, emptyList())
actions.add { methodNode.instructions.remove(inlineMarker) } actions.add { methodNode.instructions.remove(inlineMarker) }
@@ -23,7 +23,7 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue
internal class LocalVariablesManager(val context: FixStackContext, val methodNode: MethodNode) { internal class LocalVariablesManager(val context: FixStackContext, val methodNode: MethodNode) {
private class AllocatedHandle(val savedStackDescriptor: SavedStackDescriptor, var numRestoreMarkers: Int) { private class AllocatedHandle(val savedStackDescriptor: SavedStackDescriptor, var numRestoreMarkers: Int) {
fun isFullyEmitted(): Boolean = fun isFullyEmitted(): Boolean =
numRestoreMarkers == 0 numRestoreMarkers == 0
fun markRestoreNodeEmitted() { fun markRestoreNodeEmitted() {
assert(numRestoreMarkers > 0) { "Emitted more restore markers than expected for $savedStackDescriptor" } assert(numRestoreMarkers > 0) { "Emitted more restore markers than expected for $savedStackDescriptor" }
@@ -43,7 +43,11 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod
return allocateNewHandle(numRestoreStackMarkers, saveStackMarker, savedStackValues) return allocateNewHandle(numRestoreStackMarkers, saveStackMarker, savedStackValues)
} }
private fun allocateNewHandle(numRestoreStackMarkers: Int, saveStackMarker: AbstractInsnNode, savedStackValues: List<BasicValue>): SavedStackDescriptor { private fun allocateNewHandle(
numRestoreStackMarkers: Int,
saveStackMarker: AbstractInsnNode,
savedStackValues: List<BasicValue>
): SavedStackDescriptor {
if (savedStackValues.any { it.type == null }) { if (savedStackValues.any { it.type == null }) {
throw AssertionError("Uninitialized value on stack at ${methodNode.instructions.indexOf(saveStackMarker)}") throw AssertionError("Uninitialized value on stack at ${methodNode.instructions.indexOf(saveStackMarker)}")
} }
@@ -62,16 +66,19 @@ internal class LocalVariablesManager(val context: FixStackContext, val methodNod
} }
private fun getFirstUnusedLocalVariableIndex(): Int = private fun getFirstUnusedLocalVariableIndex(): Int =
allocatedHandles.values.fold(initialMaxLocals) { allocatedHandles.values.fold(initialMaxLocals) { index, handle ->
index, handle -> Math.max(index, handle.savedStackDescriptor.firstUnusedLocalVarIndex) Math.max(index, handle.savedStackDescriptor.firstUnusedLocalVarIndex)
} }
fun markRestoreStackMarkerEmitted(restoreStackMarker: AbstractInsnNode) { fun markRestoreStackMarkerEmitted(restoreStackMarker: AbstractInsnNode) {
val saveStackMarker = context.saveStackMarkerForRestoreMarker[restoreStackMarker] val saveStackMarker = context.saveStackMarkerForRestoreMarker[restoreStackMarker]
markEmitted(saveStackMarker!!) markEmitted(saveStackMarker!!)
} }
fun allocateVariablesForBeforeInlineMarker(beforeInlineMarker: AbstractInsnNode, savedStackValues: List<BasicValue>): SavedStackDescriptor { fun allocateVariablesForBeforeInlineMarker(
beforeInlineMarker: AbstractInsnNode,
savedStackValues: List<BasicValue>
): SavedStackDescriptor {
return allocateNewHandle(1, beforeInlineMarker, savedStackValues) return allocateNewHandle(1, beforeInlineMarker, savedStackValues)
} }
@@ -23,10 +23,10 @@ import org.jetbrains.org.objectweb.asm.tree.analysis.Frame
import org.jetbrains.org.objectweb.asm.tree.analysis.Value import org.jetbrains.org.objectweb.asm.tree.analysis.Value
fun <V : Value> Frame<V>.top(): V? = fun <V : Value> Frame<V>.top(): V? =
peek(0) peek(0)
fun <V : Value> Frame<V>.peek(offset: Int): V? = fun <V : Value> Frame<V>.peek(offset: Int): V? =
if (stackSize > offset) getStack(stackSize - offset - 1) else null if (stackSize > offset) getStack(stackSize - offset - 1) else null
private fun <V : Value> Frame<V>.peekWordsTo(dest: MutableList<V>, size: Int, offset0: Int = 0): Int { private fun <V : Value> Frame<V>.peekWordsTo(dest: MutableList<V>, size: Int, offset0: Int = 0): Int {
var offset = offset0 var offset = offset0
@@ -54,22 +54,22 @@ fun <V : Value> Frame<V>.peekWords(size1: Int, size2: Int): List<V>? {
} }
class SavedStackDescriptor( class SavedStackDescriptor(
val savedValues: List<BasicValue>, val savedValues: List<BasicValue>,
val firstLocalVarIndex: Int val firstLocalVarIndex: Int
) { ) {
private val savedValuesSize = savedValues.fold(0, { size, value -> size + value.size }) private val savedValuesSize = savedValues.fold(0, { size, value -> size + value.size })
val firstUnusedLocalVarIndex = firstLocalVarIndex + savedValuesSize val firstUnusedLocalVarIndex = firstLocalVarIndex + savedValuesSize
override fun toString(): String = override fun toString(): String =
"@$firstLocalVarIndex: [$savedValues]" "@$firstLocalVarIndex: [$savedValues]"
fun isNotEmpty(): Boolean = savedValues.isNotEmpty() fun isNotEmpty(): Boolean = savedValues.isNotEmpty()
} }
fun saveStack( fun saveStack(
methodNode: MethodNode, methodNode: MethodNode,
nodeToReplace: AbstractInsnNode, nodeToReplace: AbstractInsnNode,
savedStackDescriptor: SavedStackDescriptor savedStackDescriptor: SavedStackDescriptor
) { ) {
with(methodNode.instructions) { with(methodNode.instructions) {
generateStoreInstructions(methodNode, nodeToReplace, savedStackDescriptor) generateStoreInstructions(methodNode, nodeToReplace, savedStackDescriptor)
@@ -85,11 +85,11 @@ fun restoreStack(methodNode: MethodNode, location: AbstractInsnNode, savedStackD
} }
fun restoreStackWithReturnValue( fun restoreStackWithReturnValue(
methodNode: MethodNode, methodNode: MethodNode,
nodeToReplace: AbstractInsnNode, nodeToReplace: AbstractInsnNode,
savedStackDescriptor: SavedStackDescriptor, savedStackDescriptor: SavedStackDescriptor,
returnValue: BasicValue, returnValue: BasicValue,
returnValueLocalVarIndex: Int returnValueLocalVarIndex: Int
) { ) {
with(methodNode.instructions) { with(methodNode.instructions) {
insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ISTORE), returnValueLocalVarIndex)) insertBefore(nodeToReplace, VarInsnNode(returnValue.type.getOpcode(Opcodes.ISTORE), returnValueLocalVarIndex))
@@ -102,8 +102,10 @@ fun restoreStackWithReturnValue(
fun generateLoadInstructions(methodNode: MethodNode, location: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor) { fun generateLoadInstructions(methodNode: MethodNode, location: AbstractInsnNode, savedStackDescriptor: SavedStackDescriptor) {
var localVarIndex = savedStackDescriptor.firstLocalVarIndex var localVarIndex = savedStackDescriptor.firstLocalVarIndex
for (value in savedStackDescriptor.savedValues) { for (value in savedStackDescriptor.savedValues) {
methodNode.instructions.insertBefore(location, methodNode.instructions.insertBefore(
VarInsnNode(value.type.getOpcode(Opcodes.ILOAD), localVarIndex)) location,
VarInsnNode(value.type.getOpcode(Opcodes.ILOAD), localVarIndex)
)
localVarIndex += value.size localVarIndex += value.size
} }
} }
@@ -112,27 +114,31 @@ fun generateStoreInstructions(methodNode: MethodNode, location: AbstractInsnNode
var localVarIndex = savedStackDescriptor.firstUnusedLocalVarIndex var localVarIndex = savedStackDescriptor.firstUnusedLocalVarIndex
for (value in savedStackDescriptor.savedValues.asReversed()) { for (value in savedStackDescriptor.savedValues.asReversed()) {
localVarIndex -= value.size localVarIndex -= value.size
methodNode.instructions.insertBefore(location, methodNode.instructions.insertBefore(
VarInsnNode(value.type.getOpcode(Opcodes.ISTORE), localVarIndex)) location,
VarInsnNode(value.type.getOpcode(Opcodes.ISTORE), localVarIndex)
)
} }
} }
fun getPopInstruction(top: BasicValue) = fun getPopInstruction(top: BasicValue) =
InsnNode(when (top.size) { InsnNode(
1 -> Opcodes.POP when (top.size) {
2 -> Opcodes.POP2 1 -> Opcodes.POP
else -> throw AssertionError("Unexpected value type size") 2 -> Opcodes.POP2
}) else -> throw AssertionError("Unexpected value type size")
}
)
fun removeAlwaysFalseIfeq(methodNode: MethodNode, node: AbstractInsnNode) { fun removeAlwaysFalseIfeq(methodNode: MethodNode, node: AbstractInsnNode) {
with (methodNode.instructions) { with(methodNode.instructions) {
remove(node.next) remove(node.next)
remove(node) remove(node)
} }
} }
fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode) { fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode) {
with (methodNode.instructions) { with(methodNode.instructions) {
val next = node.next as JumpInsnNode val next = node.next as JumpInsnNode
insertBefore(node, JumpInsnNode(Opcodes.GOTO, next.label)) insertBefore(node, JumpInsnNode(Opcodes.GOTO, next.label))
remove(node) remove(node)
@@ -141,7 +147,7 @@ fun replaceAlwaysTrueIfeqWithGoto(methodNode: MethodNode, node: AbstractInsnNode
} }
fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List<BasicValue>) { fun replaceMarkerWithPops(methodNode: MethodNode, node: AbstractInsnNode, expectedStackSize: Int, stackContent: List<BasicValue>) {
with (methodNode.instructions) { with(methodNode.instructions) {
for (stackValue in stackContent.subList(expectedStackSize, stackContent.size)) { for (stackValue in stackContent.subList(expectedStackSize, stackContent.size)) {
insert(node, getPopInstruction(stackValue)) insert(node, getPopInstruction(stackValue))
} }
@@ -47,7 +47,7 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
} }
private fun Type?.isReferenceType() = private fun Type?.isReferenceType() =
this?.sort.let { it == Type.OBJECT || it == Type.ARRAY } this?.sort.let { it == Type.OBJECT || it == Type.ARRAY }
override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? {
val defaultResult = super.unaryOperation(insn, value) val defaultResult = super.unaryOperation(insn, value)
@@ -82,29 +82,29 @@ class NullabilityInterpreter : OptimizationBasicInterpreter() {
} }
override fun merge(v: BasicValue, w: BasicValue): BasicValue = override fun merge(v: BasicValue, w: BasicValue): BasicValue =
when { when {
v is NullBasicValue && w is NullBasicValue -> v is NullBasicValue && w is NullBasicValue ->
NullBasicValue NullBasicValue
v is NullBasicValue || w is NullBasicValue -> v is NullBasicValue || w is NullBasicValue ->
StrictBasicValue.REFERENCE_VALUE StrictBasicValue.REFERENCE_VALUE
v is ProgressionIteratorBasicValue && w is ProgressionIteratorBasicValue -> v is ProgressionIteratorBasicValue && w is ProgressionIteratorBasicValue ->
mergeNotNullValuesOfSameKind(v, w) mergeNotNullValuesOfSameKind(v, w)
v is ProgressionIteratorBasicValue && w is NotNullBasicValue -> v is ProgressionIteratorBasicValue && w is NotNullBasicValue ->
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
w is ProgressionIteratorBasicValue && v is NotNullBasicValue -> w is ProgressionIteratorBasicValue && v is NotNullBasicValue ->
NotNullBasicValue.NOT_NULL_REFERENCE_VALUE NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
v is NotNullBasicValue && w is NotNullBasicValue -> v is NotNullBasicValue && w is NotNullBasicValue ->
mergeNotNullValuesOfSameKind(v, w) mergeNotNullValuesOfSameKind(v, w)
else -> else ->
super.merge(v, w) super.merge(v, w)
} }
private fun mergeNotNullValuesOfSameKind(v: StrictBasicValue, w: StrictBasicValue) = private fun mergeNotNullValuesOfSameKind(v: StrictBasicValue, w: StrictBasicValue) =
if (v.type == w.type) v else NotNullBasicValue.NOT_NULL_REFERENCE_VALUE if (v.type == w.type) v else NotNullBasicValue.NOT_NULL_REFERENCE_VALUE
} }
fun TypeInsnNode.getObjectType(): Type = fun TypeInsnNode.getObjectType(): Type =
Type.getObjectType(desc) Type.getObjectType(desc)
@@ -37,7 +37,8 @@ import org.jetbrains.org.objectweb.asm.tree.*
class RedundantNullCheckMethodTransformer : MethodTransformer() { class RedundantNullCheckMethodTransformer : MethodTransformer() {
override fun transform(internalClassName: String, methodNode: MethodNode) { override fun transform(internalClassName: String, methodNode: MethodNode) {
while (TransformerPass(internalClassName, methodNode).run()) {} while (TransformerPass(internalClassName, methodNode).run()) {
}
} }
private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) { private class TransformerPass(val internalClassName: String, val methodNode: MethodNode) {
@@ -79,10 +80,10 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
} }
private fun AbstractInsnNode.isOptimizable() = private fun AbstractInsnNode.isOptimizable() =
opcode == Opcodes.IFNULL || opcode == Opcodes.IFNULL ||
opcode == Opcodes.IFNONNULL || opcode == Opcodes.IFNONNULL ||
opcode == Opcodes.INSTANCEOF || opcode == Opcodes.INSTANCEOF ||
isCheckExpressionValueIsNotNull() isCheckExpressionValueIsNotNull()
private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) { private fun transformTrivialChecks(nullabilityMap: Map<AbstractInsnNode, StrictBasicValue>) {
for ((insn, value) in nullabilityMap) { for ((insn, value) in nullabilityMap) {
@@ -108,8 +109,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
popReferenceValueBefore(insn) popReferenceValueBefore(insn)
if (alwaysTrue) { if (alwaysTrue) {
set(insn, JumpInsnNode(Opcodes.GOTO, insn.label)) set(insn, JumpInsnNode(Opcodes.GOTO, insn.label))
} } else {
else {
remove(insn) remove(insn)
} }
} }
@@ -120,8 +120,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
if (nullability == Nullability.NULL) { if (nullability == Nullability.NULL) {
changes = true changes = true
transformTrivialInstanceOf(insn, false) transformTrivialInstanceOf(insn, false)
} } else if (nullability == Nullability.NOT_NULL && value.type.internalName == insn.desc) {
else if (nullability == Nullability.NOT_NULL && value.type.internalName == insn.desc) {
changes = true changes = true
transformTrivialInstanceOf(insn, true) transformTrivialInstanceOf(insn, true)
} }
@@ -160,8 +159,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
val previous = insn.previous ?: continue@insnLoop val previous = insn.previous ?: continue@insnLoop
if (previous.opcode == Opcodes.ALOAD) { if (previous.opcode == Opcodes.ALOAD) {
addDependentCheck(insn, previous as VarInsnNode) addDependentCheck(insn, previous as VarInsnNode)
} } else if (previous.opcode == Opcodes.DUP) {
else if (previous.opcode == Opcodes.DUP) {
val previous2 = previous.previous ?: continue@insnLoop val previous2 = previous.previous ?: continue@insnLoop
if (previous2.opcode == Opcodes.ALOAD) { if (previous2.opcode == Opcodes.ALOAD) {
addDependentCheck(insn, previous2 as VarInsnNode) addDependentCheck(insn, previous2 as VarInsnNode)
@@ -184,8 +182,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
val insn1 = ldcInsn.previous ?: continue@insnLoop val insn1 = ldcInsn.previous ?: continue@insnLoop
if (insn1.opcode == Opcodes.ALOAD) { if (insn1.opcode == Opcodes.ALOAD) {
aLoadInsn = insn1 as VarInsnNode aLoadInsn = insn1 as VarInsnNode
} } else if (insn1.opcode == Opcodes.DUP) {
else if (insn1.opcode == Opcodes.DUP) {
val insn2 = insn1.previous ?: continue@insnLoop val insn2 = insn1.previous ?: continue@insnLoop
if (insn2.opcode == Opcodes.ALOAD) { if (insn2.opcode == Opcodes.ALOAD) {
aLoadInsn = insn2 as VarInsnNode aLoadInsn = insn2 as VarInsnNode
@@ -227,7 +224,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
Opcodes.INVOKESTATIC -> { Opcodes.INVOKESTATIC -> {
when { when {
insn.isCheckParameterIsNotNull() || insn.isCheckParameterIsNotNull() ||
insn.isCheckExpressionValueIsNotNull() -> insn.isCheckExpressionValueIsNotNull() ->
injectAssumptionsForNotNullAssertion(varIndex, insn) injectAssumptionsForNotNullAssertion(varIndex, insn)
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) -> insn.isPseudo(PseudoInsn.STORE_NOT_NULL) ->
injectCodeForStoreNotNull(insn) injectCodeForStoreNotNull(insn)
@@ -318,8 +315,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
methodNode.instructions.add(newLabel) methodNode.instructions.add(newLabel)
next.label = newLabel next.label = newLabel
insertAfterNotNull = newLabel insertAfterNotNull = newLabel
} } else {
else {
originalLabel = null originalLabel = null
insertAfterNotNull = next insertAfterNotNull = next
} }
@@ -395,41 +391,41 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
} }
internal fun AbstractInsnNode.isInstanceOfOrNullCheck() = internal fun AbstractInsnNode.isInstanceOfOrNullCheck() =
opcode == Opcodes.INSTANCEOF || opcode == Opcodes.INSTANCEOF ||
opcode == Opcodes.IFNULL || opcode == Opcodes.IFNULL ||
opcode == Opcodes.IFNONNULL opcode == Opcodes.IFNONNULL
internal fun AbstractInsnNode.isCheckParameterIsNotNull() = internal fun AbstractInsnNode.isCheckParameterIsNotNull() =
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) { isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
name == "checkParameterIsNotNull" && name == "checkParameterIsNotNull" &&
desc == "(Ljava/lang/Object;Ljava/lang/String;)V" desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
} }
internal fun AbstractInsnNode.isCheckExpressionValueIsNotNull() = internal fun AbstractInsnNode.isCheckExpressionValueIsNotNull() =
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) { isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
name == "checkExpressionValueIsNotNull" && name == "checkExpressionValueIsNotNull" &&
desc == "(Ljava/lang/Object;Ljava/lang/String;)V" desc == "(Ljava/lang/Object;Ljava/lang/String;)V"
} }
internal fun AbstractInsnNode.isThrowIntrinsic() = internal fun AbstractInsnNode.isThrowIntrinsic() =
isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) { isInsn<MethodInsnNode>(Opcodes.INVOKESTATIC) {
owner == IntrinsicMethods.INTRINSICS_CLASS_NAME && owner == IntrinsicMethods.INTRINSICS_CLASS_NAME &&
name in THROW_INTRINSIC_METHOD_NAMES name in THROW_INTRINSIC_METHOD_NAMES
} }
internal val THROW_INTRINSIC_METHOD_NAMES = internal val THROW_INTRINSIC_METHOD_NAMES =
setOf( setOf(
"throwNpe", "throwNpe",
"throwUninitializedProperty", "throwUninitializedProperty",
"throwUninitializedPropertyAccessException", "throwUninitializedPropertyAccessException",
"throwAssert", "throwAssert",
"throwIllegalArgument", "throwIllegalArgument",
"throwIllegalState", "throwIllegalState",
"throwParameterIsNullException", "throwParameterIsNullException",
"throwUndefinedForReified" "throwUndefinedForReified"
) )
internal fun InsnList.popReferenceValueBefore(insn: AbstractInsnNode) { internal fun InsnList.popReferenceValueBefore(insn: AbstractInsnNode) {
val prev = insn.previous val prev = insn.previous
@@ -37,14 +37,15 @@ object NullBasicValue : StrictBasicValue(AsmTypes.OBJECT_TYPE)
enum class Nullability { enum class Nullability {
NULL, NOT_NULL, NULLABLE; NULL, NOT_NULL, NULLABLE;
fun isNull() = this == NULL fun isNull() = this == NULL
fun isNotNull() = this == NOT_NULL fun isNotNull() = this == NOT_NULL
} }
fun BasicValue.getNullability(): Nullability = fun BasicValue.getNullability(): Nullability =
when (this) { when (this) {
is NullBasicValue -> Nullability.NULL is NullBasicValue -> Nullability.NULL
is NotNullBasicValue -> Nullability.NOT_NULL is NotNullBasicValue -> Nullability.NOT_NULL
is ProgressionIteratorBasicValue -> Nullability.NOT_NULL is ProgressionIteratorBasicValue -> Nullability.NOT_NULL
else -> Nullability.NULLABLE else -> Nullability.NULLABLE
} }
@@ -27,6 +27,6 @@ open class CompositeMethodTransformer(private val transformers: List<MethodTrans
companion object { companion object {
inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) = inline fun build(builder: MutableList<MethodTransformer>.() -> Unit) =
CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() }) CompositeMethodTransformer(ArrayList<MethodTransformer>().apply { builder() })
} }
} }