[JVM_IR] Fix offsets in constant propagation optimization.
Loads of temporary variables that contain constants are replaced with a copy of the constant. This avoids locals loads and stores. However, the copy of the constant needs to have the offset of the load and not of the original constant. Fixes KT-41963.
This commit is contained in:
committed by
Alexander Udalov
parent
f273edeb8e
commit
78483930bc
@@ -13,8 +13,10 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
|||||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrFile
|
import org.jetbrains.kotlin.ir.declarations.IrFile
|
||||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
|
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||||
|
import org.jetbrains.kotlin.ir.expressions.IrGetField
|
||||||
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl
|
||||||
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
import org.jetbrains.kotlin.ir.types.isPrimitiveType
|
||||||
import org.jetbrains.kotlin.ir.types.isStringClassType
|
import org.jetbrains.kotlin.ir.types.isStringClassType
|
||||||
@@ -41,9 +43,6 @@ fun IrField.constantValue(context: JvmBackendContext? = null): IrConst<*>? {
|
|||||||
return if (implicitConst || correspondingPropertySymbol?.owner?.isConst == true) value else null
|
return if (implicitConst || correspondingPropertySymbol?.owner?.isConst == true) value else null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <T> IrConst<T>.copyWithOffsets(startOffset: Int, endOffset: Int) =
|
|
||||||
IrConstImpl(startOffset, endOffset, type, kind, value)
|
|
||||||
|
|
||||||
class ConstLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
class ConstLowering(val context: JvmBackendContext) : IrElementTransformerVoid(), FileLoweringPass {
|
||||||
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
override fun lower(irFile: IrFile) = irFile.transformChildrenVoid()
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -347,7 +347,7 @@ class JvmOptimizationLowering(val context: JvmBackendContext) : FileLoweringPass
|
|||||||
// initializer with the constant initializer.
|
// initializer with the constant initializer.
|
||||||
val variable = expression.symbol.owner
|
val variable = expression.symbol.owner
|
||||||
return if (isImmutableTemporaryVariableWithConstantValue(variable))
|
return if (isImmutableTemporaryVariableWithConstantValue(variable))
|
||||||
((variable as IrVariable).initializer!! as IrConst<*>).copy()
|
((variable as IrVariable).initializer!! as IrConst<*>).copyWithOffsets(expression.startOffset, expression.endOffset)
|
||||||
else
|
else
|
||||||
expression
|
expression
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ abstract class IrConst<T> : IrExpression(), IrExpressionWithCopy {
|
|||||||
abstract val value: T
|
abstract val value: T
|
||||||
|
|
||||||
abstract override fun copy(): IrConst<T>
|
abstract override fun copy(): IrConst<T>
|
||||||
|
abstract fun copyWithOffsets(startOffset: Int, endOffset: Int): IrConst<T>
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class IrConstKind<T>(val asString: kotlin.String) {
|
sealed class IrConstKind<T>(val asString: kotlin.String) {
|
||||||
|
|||||||
@@ -34,6 +34,9 @@ class IrConstImpl<T>(
|
|||||||
override fun copy(): IrConst<T> =
|
override fun copy(): IrConst<T> =
|
||||||
IrConstImpl(startOffset, endOffset, type, kind, value)
|
IrConstImpl(startOffset, endOffset, type, kind, value)
|
||||||
|
|
||||||
|
override fun copyWithOffsets(startOffset: Int, endOffset: Int) =
|
||||||
|
IrConstImpl(startOffset, endOffset, type, kind, value)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun string(startOffset: Int, endOffset: Int, type: IrType, value: String): IrConstImpl<String> =
|
fun string(startOffset: Int, endOffset: Int, type: IrType, value: String): IrConstImpl<String> =
|
||||||
IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value)
|
IrConstImpl(startOffset, endOffset, type, IrConstKind.String, value)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// FILE: test.kt
|
||||||
|
|
||||||
|
fun box() {
|
||||||
|
when (1) {
|
||||||
|
2 ->
|
||||||
|
"2"
|
||||||
|
3 ->
|
||||||
|
"3"
|
||||||
|
else ->
|
||||||
|
"1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JVM_IR and JVM backends have different heuristics for when to use a switch.
|
||||||
|
// JVM_IR does not use a switch in this case and therefore steps to the evaluation
|
||||||
|
// of the condition for each of the cases.
|
||||||
|
|
||||||
|
// LINENUMBERS
|
||||||
|
// test.kt:4 box
|
||||||
|
// LINENUMBERS JVM_IR
|
||||||
|
// test.kt:5 box
|
||||||
|
// test.kt:7 box
|
||||||
|
// LINENUMBERS
|
||||||
|
// test.kt:10 box
|
||||||
|
// test.kt:12 box
|
||||||
+6
@@ -433,6 +433,12 @@ public class IrSteppingTestGenerated extends AbstractIrSteppingTest {
|
|||||||
runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt");
|
runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("whenConstant.kt")
|
||||||
|
public void testWhenConstant() throws Exception {
|
||||||
|
runTest("compiler/testData/debug/stepping/whenConstant.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("whenIsChecks.kt")
|
@TestMetadata("whenIsChecks.kt")
|
||||||
public void testWhenIsChecks() throws Exception {
|
public void testWhenIsChecks() throws Exception {
|
||||||
|
|||||||
+6
@@ -433,6 +433,12 @@ public class SteppingTestGenerated extends AbstractSteppingTest {
|
|||||||
runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt");
|
runTest("compiler/testData/debug/stepping/whenComplicatedSubject.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("whenConstant.kt")
|
||||||
|
public void testWhenConstant() throws Exception {
|
||||||
|
runTest("compiler/testData/debug/stepping/whenConstant.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("whenIsChecks.kt")
|
@TestMetadata("whenIsChecks.kt")
|
||||||
public void testWhenIsChecks() throws Exception {
|
public void testWhenIsChecks() throws Exception {
|
||||||
|
|||||||
Vendored
+43
@@ -0,0 +1,43 @@
|
|||||||
|
LineBreakpoint created at stepOverWhenWithInline.kt:5
|
||||||
|
Run Java
|
||||||
|
Connected to the target VM
|
||||||
|
stepOverWhenWithInline.kt:5
|
||||||
|
stepOverWhenWithInline.kt:7
|
||||||
|
stepOverWhenWithInline.kt:8
|
||||||
|
stepOverWhenWithInline.kt:9
|
||||||
|
stepOverWhenWithInline.kt:7
|
||||||
|
stepOverWhenWithInline.kt:13
|
||||||
|
stepOverWhenWithInline.kt:14
|
||||||
|
stepOverWhenWithInline.kt:17
|
||||||
|
stepOverWhenWithInline.kt:18
|
||||||
|
stepOverWhenWithInline.kt:13
|
||||||
|
stepOverWhenWithInline.kt:25
|
||||||
|
stepOverWhenWithInline.kt:26
|
||||||
|
stepOverWhenWithInline.kt:27
|
||||||
|
stepOverWhenWithInline.kt:25
|
||||||
|
stepOverWhenWithInline.kt:32
|
||||||
|
stepOverWhenWithInline.kt:33
|
||||||
|
stepOverWhenWithInline.kt:34
|
||||||
|
stepOverWhenWithInline.kt:32
|
||||||
|
stepOverWhenWithInline.kt:38
|
||||||
|
stepOverWhenWithInline.kt:39
|
||||||
|
stepOverWhenWithInline.kt:42
|
||||||
|
stepOverWhenWithInline.kt:43
|
||||||
|
stepOverWhenWithInline.kt:38
|
||||||
|
stepOverWhenWithInline.kt:51
|
||||||
|
stepOverWhenWithInline.kt:52
|
||||||
|
stepOverWhenWithInline.kt:51
|
||||||
|
stepOverWhenWithInline.kt:57
|
||||||
|
stepOverWhenWithInline.kt:58
|
||||||
|
stepOverWhenWithInline.kt:57
|
||||||
|
stepOverWhenWithInline.kt:63
|
||||||
|
stepOverWhenWithInline.kt:64
|
||||||
|
stepOverWhenWithInline.kt:65
|
||||||
|
stepOverWhenWithInline.kt:63
|
||||||
|
stepOverWhenWithInline.kt:75
|
||||||
|
stepOverWhenWithInline.kt:76
|
||||||
|
stepOverWhenWithInline.kt:75
|
||||||
|
stepOverWhenWithInline.kt:80
|
||||||
|
Disconnected from the target VM
|
||||||
|
|
||||||
|
Process finished with exit code 0
|
||||||
+4
-1
@@ -86,4 +86,7 @@ inline fun foo(f: () -> Int): Int {
|
|||||||
|
|
||||||
fun test(i: Int) = i
|
fun test(i: Int) = i
|
||||||
|
|
||||||
// STEP_OVER: 32
|
// STEP_OVER: 36
|
||||||
|
|
||||||
|
// JVM_IR and JVM backends have different heuristics for when to use a table switch.
|
||||||
|
// This results is minor differences in step over behavior.
|
||||||
+1
-1
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
LineBreakpoint created at stepOverWhenWithInline.kt:5
|
LineBreakpoint created at stepOverWhenWithInline.kt:5
|
||||||
Run Java
|
Run Java
|
||||||
Connected to the target VM
|
Connected to the target VM
|
||||||
@@ -35,6 +34,7 @@ stepOverWhenWithInline.kt:63
|
|||||||
stepOverWhenWithInline.kt:75
|
stepOverWhenWithInline.kt:75
|
||||||
stepOverWhenWithInline.kt:76
|
stepOverWhenWithInline.kt:76
|
||||||
stepOverWhenWithInline.kt:75
|
stepOverWhenWithInline.kt:75
|
||||||
|
stepOverWhenWithInline.kt:80
|
||||||
Disconnected from the target VM
|
Disconnected from the target VM
|
||||||
|
|
||||||
Process finished with exit code 0
|
Process finished with exit code 0
|
||||||
|
|||||||
Reference in New Issue
Block a user