Recalculate max stack on method emitting: optimizations could change it

This commit is contained in:
Mikhael Bogdanov
2017-09-04 12:00:36 +02:00
parent 638cf346aa
commit 81a1bf3319
4 changed files with 30 additions and 15 deletions
@@ -16,10 +16,12 @@
package org.jetbrains.kotlin.codegen.optimization.common package org.jetbrains.kotlin.codegen.optimization.common
import org.jetbrains.kotlin.codegen.inline.MaxStackFrameSizeAndLocalsCalculator
import org.jetbrains.kotlin.codegen.inline.insnText import org.jetbrains.kotlin.codegen.inline.insnText
import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext import org.jetbrains.kotlin.codegen.optimization.removeNodeGetNext
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsn
import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.kotlin.utils.addToStdlib.safeAs
import org.jetbrains.org.objectweb.asm.MethodVisitor
import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Opcodes
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
@@ -71,6 +73,14 @@ fun MethodNode.prepareForEmitting() {
current = prev current = prev
} }
maxStack = -1
accept(MaxStackFrameSizeAndLocalsCalculator(
Opcodes.ASM5, access, desc,
object : MethodVisitor(Opcodes.ASM5) {
override fun visitMaxs(maxStack: Int, maxLocals: Int) {
this@prepareForEmitting.maxStack = maxStack
}
}))
} }
fun MethodNode.stripOptimizationMarkers() { fun MethodNode.stripOptimizationMarkers() {
@@ -46,14 +46,13 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
private var changes = false private var changes = false
fun run(): Boolean { fun run(): Boolean {
val stackOnThrowExceptions = hashMapOf<AbstractInsnNode, Int>() val checkedReferenceTypes = analyzeTypesAndRemoveDeadCode()
val checkedReferenceTypes = analyzeTypesAndRemoveDeadCode(stackOnThrowExceptions) eliminateRedundantChecks(checkedReferenceTypes)
eliminateRedundantChecks(checkedReferenceTypes, stackOnThrowExceptions)
return changes return changes
} }
private fun analyzeTypesAndRemoveDeadCode(stackOnThrowExceptionsHolder: MutableMap<AbstractInsnNode, Int>): Map<AbstractInsnNode, Type> { private fun analyzeTypesAndRemoveDeadCode(): Map<AbstractInsnNode, Type> {
val insns = methodNode.instructions.toArray() val insns = methodNode.instructions.toArray()
val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter()) val frames = analyze(internalClassName, methodNode, OptimizationBasicInterpreter())
@@ -67,8 +66,6 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
insn.isCheckParameterIsNotNull() || insn.isCheckParameterIsNotNull() ||
insn.isCheckExpressionValueIsNotNull() -> insn.isCheckExpressionValueIsNotNull() ->
relevantReferenceTypes[insn] = frame.peek(1)?.type ?: continue@insnLoop relevantReferenceTypes[insn] = frame.peek(1)?.type ?: continue@insnLoop
insn.isThrowIntrinsicWithoutArguments() ->
stackOnThrowExceptionsHolder[insn] = frame.maxStackSize
insn.isPseudo(PseudoInsn.STORE_NOT_NULL) -> { insn.isPseudo(PseudoInsn.STORE_NOT_NULL) -> {
val previous = insn.previous ?: continue@insnLoop val previous = insn.previous ?: continue@insnLoop
if (previous.opcode != Opcodes.ASTORE) continue@insnLoop if (previous.opcode != Opcodes.ASTORE) continue@insnLoop
@@ -87,10 +84,9 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
} }
private fun eliminateRedundantChecks( private fun eliminateRedundantChecks(
checkedReferenceTypes: Map<AbstractInsnNode, Type>, checkedReferenceTypes: Map<AbstractInsnNode, Type>
stackOnThrowExceptions: MutableMap<AbstractInsnNode, Int>
) { ) {
val nullabilityAssumptions = injectNullabilityAssumptions(checkedReferenceTypes, stackOnThrowExceptions) val nullabilityAssumptions = injectNullabilityAssumptions(checkedReferenceTypes)
val nullabilityMap = analyzeNullabilities() val nullabilityMap = analyzeNullabilities()
@@ -100,9 +96,8 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
} }
private fun injectNullabilityAssumptions( private fun injectNullabilityAssumptions(
checkedReferenceTypes: Map<AbstractInsnNode, Type>, checkedReferenceTypes: Map<AbstractInsnNode, Type>
stackOnThrowExceptions: MutableMap<AbstractInsnNode, Int> ) = NullabilityAssumptionsBuilder(checkedReferenceTypes).injectNullabilityAssumptions()
) = NullabilityAssumptionsBuilder(checkedReferenceTypes, stackOnThrowExceptions).injectNullabilityAssumptions()
private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> { private fun analyzeNullabilities(): Map<AbstractInsnNode, StrictBasicValue> {
val frames = analyze(internalClassName, methodNode, NullabilityInterpreter()) val frames = analyze(internalClassName, methodNode, NullabilityInterpreter())
@@ -186,8 +181,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
} }
private inner class NullabilityAssumptionsBuilder( private inner class NullabilityAssumptionsBuilder(
val relatedReferenceTypes: Map<AbstractInsnNode, Type>, val relatedReferenceTypes: Map<AbstractInsnNode, Type>
val stackOnThrowExceptions: MutableMap<AbstractInsnNode, Int>
) { ) {
private val checksDependingOnVariable = HashMap<Int, MutableList<AbstractInsnNode>>() private val checksDependingOnVariable = HashMap<Int, MutableList<AbstractInsnNode>>()
@@ -387,7 +381,7 @@ class RedundantNullCheckMethodTransformer : MethodTransformer() {
}) })
} }
methodNode.maxStack = Math.max(methodNode.maxStack, (stackOnThrowExceptions[insn] ?: -1) + 1) methodNode.maxStack = methodNode.maxStack + 1 //will be recalculated in prepareForEmitting
} }
private fun NullabilityAssumptions.injectCodeForStoreNotNull(insn: AbstractInsnNode, varType: Type) { private fun NullabilityAssumptions.injectCodeForStoreNotNull(insn: AbstractInsnNode, varType: Type) {
@@ -0,0 +1,5 @@
fun test() {
1
}
// 1 MAXSTACK = 0
@@ -300,6 +300,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("maxStackAfterOptimizations.kt")
public void testMaxStackAfterOptimizations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/maxStackAfterOptimizations.kt");
doTest(fileName);
}
@TestMetadata("noFlagAnnotations.kt") @TestMetadata("noFlagAnnotations.kt")
public void testNoFlagAnnotations() throws Exception { public void testNoFlagAnnotations() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/noFlagAnnotations.kt");