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:
+31
@@ -17,6 +17,7 @@
|
|||||||
package org.jetbrains.kotlin.codegen.optimization.captured
|
package org.jetbrains.kotlin.codegen.optimization.captured
|
||||||
|
|
||||||
import org.jetbrains.kotlin.builtins.PrimitiveType
|
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.removeEmptyCatchBlocks
|
||||||
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
import org.jetbrains.kotlin.codegen.optimization.common.removeUnusedLocalVariables
|
||||||
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
import org.jetbrains.kotlin.codegen.optimization.fixStack.peek
|
||||||
@@ -63,6 +64,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
val stackInsns: MutableCollection<AbstractInsnNode> = LinkedHashSet()
|
val stackInsns: MutableCollection<AbstractInsnNode> = LinkedHashSet()
|
||||||
val getFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
val getFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||||
val putFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
val putFieldInsns: MutableCollection<FieldInsnNode> = LinkedHashSet()
|
||||||
|
var cleanVarInstruction: VarInsnNode? = null
|
||||||
|
|
||||||
fun canRewrite(): Boolean =
|
fun canRewrite(): Boolean =
|
||||||
!hazard &&
|
!hazard &&
|
||||||
@@ -198,6 +200,7 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
for (refValue in refValues) {
|
for (refValue in refValues) {
|
||||||
if (refValue.hazard) continue
|
if (refValue.hazard) continue
|
||||||
val localVar = refValue.localVar ?: continue
|
val localVar = refValue.localVar ?: continue
|
||||||
|
val oldVarIndex = localVar.index
|
||||||
|
|
||||||
if (refValue.valueType.size != 1) {
|
if (refValue.valueType.size != 1) {
|
||||||
refValue.localVarIndex = methodNode.maxLocals
|
refValue.localVarIndex = methodNode.maxLocals
|
||||||
@@ -214,9 +217,30 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
refValue.hazard = true
|
refValue.hazard = true
|
||||||
continue
|
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() {
|
private fun rewrite() {
|
||||||
for (refValue in refValues) {
|
for (refValue in refValues) {
|
||||||
if (!refValue.canRewrite()) continue
|
if (!refValue.canRewrite()) continue
|
||||||
@@ -248,6 +272,13 @@ class CapturedVarsOptimizationMethodTransformer : MethodTransformer() {
|
|||||||
capturedVar.putFieldInsns.forEach {
|
capturedVar.putFieldInsns.forEach {
|
||||||
set(it, VarInsnNode(capturedVar.valueType.getOpcode(Opcodes.ISTORE), capturedVar.localVarIndex))
|
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"
|
||||||
|
}
|
||||||
+6
@@ -3859,6 +3859,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||||
|
|||||||
@@ -3859,6 +3859,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||||
|
|||||||
@@ -3859,6 +3859,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||||
|
|||||||
@@ -4532,6 +4532,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("sharedSlotsWithCapturedVars.kt")
|
||||||
public void testSharedSlotsWithCapturedVars() throws Exception {
|
public void testSharedSlotsWithCapturedVars() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturedVarsOptimization/sharedSlotsWithCapturedVars.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user