Delete clean reference instructions on dereferencing captured variables

Codegen generates clean instructions for ref values (captured vars)
on block exit so we should delete them on dereferencing captured values.

  #KT-17200 FIXED
This commit is contained in:
Mikhael Bogdanov
2017-04-07 11:47:18 +02:00
parent f3e2abfd5f
commit b51cb9a911
6 changed files with 81 additions and 0 deletions
@@ -17,6 +17,7 @@
package org.jetbrains.kotlin.codegen.optimization.captured
import org.jetbrains.kotlin.builtins.PrimitiveType
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
@@ -63,6 +64,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
val stackInsns: MutableCollection<AbstractInsnNode> = LinkedHashSet()
val getFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
val putFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
var cleanVarInstruction: VarInsnNode? = null
fun canRewrite(): Boolean =
!hazard &&
@@ -198,6 +200,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
for (refValue in refValues) {
if (refValue.hazard) continue
val localVar = refValue.localVar ?: continue
val oldVarIndex = localVar.index
if (refValue.valueType.size != 1) {
refValue.localVarIndex = methodNode.maxLocals
@@ -214,9 +217,30 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
refValue.hazard = true
continue
}
val cleanInstructions = findCleanInstructions(refValue, oldVarIndex, methodNode.instructions)
if (cleanInstructions.size > 1 ) {
refValue.hazard = true
continue
}
refValue.cleanVarInstruction = cleanInstructions.firstOrNull()
}
}
private fun findCleanInstructions(refValue: CapturedVarDescriptor, oldVarIndex: Int, instructions: InsnList): List<VarInsnNode> {
val cleanInstructions =
InsnSequence(instructions).filterIsInstance<VarInsnNode>().filter {
it.`var` == oldVarIndex
}.filter {
it.previous?.opcode == Opcodes.ACONST_NULL
}.filter {
val operationIndex = instructions.indexOf(it)
val localVariableNode = refValue.localVar!!
instructions.indexOf(localVariableNode.start) < operationIndex && operationIndex < instructions.indexOf(localVariableNode.end)
}.toList()
return cleanInstructions
}
private fun rewrite() {
for (refValue in refValues) {
if (!refValue.canRewrite()) continue
@@ -248,6 +272,13 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
capturedVar.putFieldInsns.forEach {
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex))
}
//after visiting block codegen tries to delete all allocated references:
// see ExpressionCodegen.addLeaveTaskToRemoveLocalVariableFromFrameMap
capturedVar.cleanVarInstruction?.let {
remove(it.previous)
remove(it)
}
}
}
}
@@ -0,0 +1,26 @@
inline fun inlineCall(action: () -> Unit) {
action()
}
fun test() {
var width = 1
inlineCall {
width += width
}
}
fun test2() {
var width = 1L
val newValue = 1;
val newValue2 = "123";
val newValue3 = 2.0;
inlineCall {
width += width
}
}
fun box(): String {
test()
test2()
return "OK"
}
@@ -3859,6 +3859,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName);
}
@TestMetadata("kt17200.kt")
public void testKt17200() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt");
doTest(fileName);
}
@TestMetadata("sharedSlotsWithCapturedVars.kt")
public void testSharedSlotsWithCapturedVars() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
@@ -3859,6 +3859,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName);
}
@TestMetadata("kt17200.kt")
public void testKt17200() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt");
doTest(fileName);
}
@TestMetadata("sharedSlotsWithCapturedVars.kt")
public void testSharedSlotsWithCapturedVars() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
@@ -3859,6 +3859,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName);
}
@TestMetadata("kt17200.kt")
public void testKt17200() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt");
doTest(fileName);
}
@TestMetadata("sharedSlotsWithCapturedVars.kt")
public void testSharedSlotsWithCapturedVars() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
@@ -4532,6 +4532,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName);
}
@TestMetadata("kt17200.kt")
public void testKt17200() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/kt17200.kt");
doTest(fileName);
}
@TestMetadata("sharedSlotsWithCapturedVars.kt")
public void testSharedSlotsWithCapturedVars() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");