Recalculate max stack on method emitting: optimizations could change it
This commit is contained in:
@@ -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() {
|
||||||
|
|||||||
+9
-15
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user