[JVM] Fix the nop removal optimization.
Never remove a nop if that would cause a line number to move across a local lifetime boundary. This fixes KT-42725.
This commit is contained in:
+16
-23
@@ -16,7 +16,6 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.codegen.optimization
|
package org.jetbrains.kotlin.codegen.optimization
|
||||||
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.InsnSequence
|
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.findNextOrNull
|
import org.jetbrains.kotlin.codegen.optimization.common.findNextOrNull
|
||||||
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.kotlin.codegen.optimization.transformer.MethodTransformer
|
||||||
@@ -53,41 +52,35 @@ class RedundantNopsCleanupMethodTransformer : MethodTransformer() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun recordNopsRequiredForDebugger(methodNode: MethodNode, requiredNops: MutableSet<AbstractInsnNode>) {
|
private fun recordNopsRequiredForDebugger(methodNode: MethodNode, requiredNops: MutableSet<AbstractInsnNode>) {
|
||||||
// We two subsets of labels that are "special" for the debugger:
|
// We consider two subsets of labels that are special for the debugger:
|
||||||
|
//
|
||||||
// 1) Labels for line numbers.
|
// 1) Labels for line numbers.
|
||||||
// 2) Labels for observable local variables lifetimes.
|
// 2) Labels for observable local variables lifetimes.
|
||||||
// NB this includes synthetic variables denoting inlined function bodies and arguments
|
// NB this includes synthetic variables denoting inlined function bodies and arguments
|
||||||
// (see JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION, JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT).
|
// (see JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_FUNCTION, JvmAbi.LOCAL_VARIABLE_NAME_PREFIX_INLINE_ARGUMENT).
|
||||||
//
|
//
|
||||||
// If we enumerate labels in a given subset in order of occurrence in the method code:
|
// We must not remove nops if that leads to a line number having no instructions before the next line number
|
||||||
// L[0], L[1], ..., L[n-1], L[n]
|
// or a linenumber crossing a local variable lifetime boundary.
|
||||||
// then for each k, 1 <= k <= n-1:
|
//
|
||||||
// an instruction interval I[k] = [L[k]; L[k+1]) should contain at least one bytecode instruction (which can be a NOP).
|
// We enumerate all special labels, and make sure to leave nops if they are the only real
|
||||||
|
// instruction between a line number label and any other special label.
|
||||||
for (insn in methodNode.instructions) {
|
val specialLabels = run {
|
||||||
if (insn is LineNumberNode) {
|
val localVarLables = hashSetOf<LabelNode>().apply {
|
||||||
val nextLineNumber = insn.findNextOrNull { it is LineNumberNode && it.line != insn.line }
|
|
||||||
requiredNops.addIfNotNull(getRequiredNopInRange(insn, nextLineNumber))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val localVariableLabels = run {
|
|
||||||
val labels = hashSetOf<LabelNode>().apply {
|
|
||||||
for (localVariable in methodNode.localVariables) {
|
for (localVariable in methodNode.localVariables) {
|
||||||
add(localVariable.start)
|
add(localVariable.start)
|
||||||
add(localVariable.end)
|
add(localVariable.end)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
methodNode.instructions.toArray().filter { labels.contains(it) }
|
methodNode.instructions.toArray().filter { localVarLables.contains(it) || it is LineNumberNode }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (i in 0..specialLabels.size - 2) {
|
||||||
for (i in 0..localVariableLabels.size - 2) {
|
val begin = specialLabels[i]
|
||||||
val begin = localVariableLabels[i]
|
val end = specialLabels[i + 1]
|
||||||
val end = localVariableLabels[i + 1]
|
if (begin is LineNumberNode) {
|
||||||
if (InsnSequence(begin, end).any { it in requiredNops }) continue
|
requiredNops.addIfNotNull(getRequiredNopInRange(begin, end))
|
||||||
requiredNops.addIfNotNull(getRequiredNopInRange(begin, end))
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -23,4 +23,4 @@ fun simpleFunVoid(f: () -> Unit): Unit {
|
|||||||
return f()
|
return f()
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3 NOP
|
// 0 NOP
|
||||||
|
|||||||
Vendored
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
LineBreakpoint created at stepOverInlinedLambdaStdlib.kt:5
|
LineBreakpoint created at stepOverInlinedLambdaStdlib.kt:5
|
||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ org.jetbrains.kotlin.util.KotlinVersionsTest.testVersionsAreConsistent, KT-35567
|
|||||||
org.jetbrains.kotlin.jvm.compiler.CompileKotlinAgainstJavaTestGenerated.WithAPT.testClassWithTypeParameter, KT-36448,,
|
org.jetbrains.kotlin.jvm.compiler.CompileKotlinAgainstJavaTestGenerated.WithAPT.testClassWithTypeParameter, KT-36448,,
|
||||||
org.jetbrains.kotlin.jvm.compiler.CompileKotlinAgainstJavaTestGenerated.WithoutAPT.testClassWithTypeParameter, KT-36448,,
|
org.jetbrains.kotlin.jvm.compiler.CompileKotlinAgainstJavaTestGenerated.WithoutAPT.testClassWithTypeParameter, KT-36448,,
|
||||||
org.jetbrains.kotlin.idea.debugger.test.AsyncStackTraceTestGenerated.testAsyncLambdas, redesign test AsyncStackTraces,,
|
org.jetbrains.kotlin.idea.debugger.test.AsyncStackTraceTestGenerated.testAsyncLambdas, redesign test AsyncStackTraces,,
|
||||||
org.jetbrains.kotlin.idea.debugger.test.KotlinSteppingTestGenerated.StepOver.testStepOverInlinedLambdaStdlib, fails after advancing bootstrap KT-37879,,
|
|
||||||
org.jetbrains.kotlin.incremental.IncrementalJsKlibCompilerRunnerTestGenerated.ClassHierarchyAffected.testMethodRemoved, full fledged FO in klib required,,
|
org.jetbrains.kotlin.incremental.IncrementalJsKlibCompilerRunnerTestGenerated.ClassHierarchyAffected.testMethodRemoved, full fledged FO in klib required,,
|
||||||
org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.ConvertToAnonymousObject.testHasConcreateMember2, Unprocessed,,
|
org.jetbrains.kotlin.idea.quickfix.QuickFixTestGenerated.ConvertToAnonymousObject.testHasConcreateMember2, Unprocessed,,
|
||||||
org.jetbrains.uast.test.kotlin.KotlinUastTypesTest.testEa101715, Unprocessed,,
|
org.jetbrains.uast.test.kotlin.KotlinUastTypesTest.testEa101715, Unprocessed,,
|
||||||
@@ -83,4 +82,4 @@ org.jetbrains.kotlin.asJava.classes.UltraLightClassSanityTestGenerated.testAnnot
|
|||||||
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.Facades.testSingleJvmClassName, Invalid behavior of old lightclasses in common tests,,
|
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.Facades.testSingleJvmClassName, Invalid behavior of old lightclasses in common tests,,
|
||||||
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testActualTypeAliasCustomJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testActualTypeAliasCustomJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
||||||
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
org.jetbrains.kotlin.idea.caches.resolve.IdeLightClassTestGenerated.CompilationErrors.testJvmPackageName, Invalid behavior of old lightclasses in common tests,,
|
||||||
org.jetbrains.uast.test.kotlin.SimpleKotlinRenderLogTest.testReceiverFun, Analysing of facade annotation with receiver site is broken (connected with KT-40403),,
|
org.jetbrains.uast.test.kotlin.SimpleKotlinRenderLogTest.testReceiverFun, Analysing of facade annotation with receiver site is broken (connected with KT-40403),,
|
||||||
|
|||||||
|
Reference in New Issue
Block a user