Do not ignore copy operations while finding source instructions

in redundant locals elimination.

Since IgnoringCopySourceInterpreter ignores ALOADs and ASTOREs,
the source instruction of ALOAD K in {ALOAD N, ASTORE K, ALOAD K}
sequence is not ASTORE K. In this case we cannot simply replace K
with N, since there can be multiple {ALOAD N, ASTORE K} sequences
in separate branches. After replacement we get different stack
frames.
This change resolves this.

However, in ReturnUnitMethodTransformer we want to ignore copies
of the same GETSTATIC kotlin/Unit.INSTANCE, since we do not mess
with local variables and just replace ASTORE with ARETURN to help
tail-call optimization.
 #KT-23373: Fixed
This commit is contained in:
Ilmir Usmanov
2018-03-21 23:52:57 +03:00
parent 15b46cdda9
commit 481dbee96a
7 changed files with 114 additions and 5 deletions
@@ -122,7 +122,7 @@ class RedundantLocalsEliminationMethodTransformer : MethodTransformer() {
val succ = findImmediateSuccessors(insn, cfg, methodNode).singleOrNull() ?: continue
if (succ.opcode != Opcodes.POP) continue
if (insn.opcode == Opcodes.ALOAD && methodNode.localVariables.firstOrNull { it.index == insn.localIndex() } != null) continue
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ)).values.flatten()
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ), ignoreCopy = false).values.flatten()
if (sources.size != 1) continue
res[insn] = succ
}
@@ -182,7 +182,7 @@ class RedundantLocalsEliminationMethodTransformer : MethodTransformer() {
it.opcode == Opcodes.ASTORE && it.localIndex() == succ.localIndex()
} != 1) continue
if (!ignoreLocalVariableTable && methodNode.localVariables.firstOrNull { it.index == succ.localIndex() } != null) continue
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ)).values.flatten()
val sources = findSourceInstructions(internalClassName, methodNode, listOf(succ), ignoreCopy = false).values.flatten()
if (sources.size > 1) continue
res[insn] = succ
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.utils.keysToMap
import org.jetbrains.kotlin.utils.sure
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.tree.*
import org.jetbrains.org.objectweb.asm.tree.analysis.SourceInterpreter
/*
* Replace POP with ARETURN iff
@@ -47,7 +48,7 @@ object ReturnUnitMethodTransformer : MethodTransformer() {
val pops = methodNode.instructions.asSequence().filter { it.opcode == Opcodes.POP }.toList()
val popSuccessors = findSuccessors(methodNode, pops)
val sourceInsns = findSourceInstructions(internalClassName, methodNode, pops)
val sourceInsns = findSourceInstructions(internalClassName, methodNode, pops, ignoreCopy = true)
val safePops = filterOutUnsafes(popSuccessors, units, sourceInsns)
// Replace POP with ARETURN for tail call optimization
@@ -122,9 +123,14 @@ object ReturnUnitMethodTransformer : MethodTransformer() {
internal fun findSourceInstructions(
internalClassName: String,
methodNode: MethodNode,
insns: Collection<AbstractInsnNode>
insns: Collection<AbstractInsnNode>,
ignoreCopy: Boolean
): Map<AbstractInsnNode, Collection<AbstractInsnNode>> {
val frames = MethodTransformer.analyze(internalClassName, methodNode, IgnoringCopyOperationSourceInterpreter())
val frames = MethodTransformer.analyze(
internalClassName,
methodNode,
if (ignoreCopy) IgnoringCopyOperationSourceInterpreter() else SourceInterpreter()
)
return insns.keysToMap {
val index = methodNode.instructions.indexOf(it)
if (isUnreachable(index, frames)) return@keysToMap emptySet<AbstractInsnNode>()
@@ -0,0 +1,43 @@
// WITH_RUNTIME
// WITH_COROUTINES
import helpers.*
import kotlin.coroutines.experimental.*
private var prevSender: String = "FAIL"
class ChatServer {
suspend fun who(sender: String) {
prevSender = sender
}
suspend fun sendTo(recipient: String, sender: String, message: String) { }
suspend fun message(sender: String, message: String) { }
}
private val server = ChatServer()
private suspend fun receivedMessage(id: String, command: String) {
when {
command.startsWith("/who") -> server.who(id)
command.startsWith("/user") -> {
val newName = command.removePrefix("/user").trim()
when {
newName.isEmpty() -> server.sendTo(id, "server::help", "/user [newName]")
else -> server.message(id, newName)
}
}
command.startsWith("/") -> server.sendTo(id, "server::help", "Unknown command ${command.takeWhile { !it.isWhitespace() }}")
else -> server.message(id, command)
}
}
fun builder(c: suspend () -> Unit) {
c.startCoroutine(EmptyContinuation)
}
fun box(): String {
builder {
receivedMessage("OK", "/who")
}
return prevSender
}
@@ -6514,6 +6514,21 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantLocalsElimination extends AbstractIrBlackBoxCodegenTest {
public void testAllFilesPresentInRedundantLocalsElimination() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/redundantLocalsElimination"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("ktor_receivedMessage.kt")
public void testKtor_receivedMessage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination/ktor_receivedMessage.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -6514,6 +6514,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantLocalsElimination extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInRedundantLocalsElimination() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/redundantLocalsElimination"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("ktor_receivedMessage.kt")
public void testKtor_receivedMessage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination/ktor_receivedMessage.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -6514,6 +6514,21 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantLocalsElimination extends AbstractLightAnalysisModeTest {
public void testAllFilesPresentInRedundantLocalsElimination() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/redundantLocalsElimination"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("ktor_receivedMessage.kt")
public void testKtor_receivedMessage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination/ktor_receivedMessage.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
@@ -7810,6 +7810,21 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class RedundantLocalsElimination extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInRedundantLocalsElimination() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/redundantLocalsElimination"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
}
@TestMetadata("ktor_receivedMessage.kt")
public void testKtor_receivedMessage() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/redundantLocalsElimination/ktor_receivedMessage.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/coroutines/stackUnwinding")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)