JVM_IR more aggressive if-null expressions fusion

Assume that changing a return type from T to T?
is not a binary-compatible change.
This commit is contained in:
Dmitry Petrov
2021-08-04 18:53:06 +03:00
committed by teamcityserver
parent 6f0bf766f2
commit 02d8c7527e
15 changed files with 206 additions and 18 deletions
@@ -1850,6 +1850,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisAndEnhancedNullability.kt")
public void testSafeCallWithElvisAndEnhancedNullability() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -730,6 +730,12 @@ public class FirBytecodeTextTestGenerated extends AbstractFirBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("severalInlines.kt")
public void testSeveralInlines() throws Exception {
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullable
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.isTrivial
import org.jetbrains.kotlin.ir.util.shallowCopy
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
@@ -33,10 +32,10 @@ val ifNullExpressionsFusionPhase =
class IfNullExpressionsFusionLowering(val context: CommonBackendContext) : FileLoweringPass {
override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(Transformer(irFile))
irFile.transformChildrenVoid(Transformer())
}
private inner class Transformer(private val currentFile: IrFile) : IrElementTransformerVoid() {
private inner class Transformer : IrElementTransformerVoid() {
override fun visitBlock(expression: IrBlock): IrExpression =
visitExpression(expression.fuseIfNull())
@@ -131,30 +130,34 @@ class IfNullExpressionsFusionLowering(val context: CommonBackendContext) : FileL
private fun IrExpression.isNull(knownVariableSymbol: IrVariableSymbol, knownVariableIsNull: Boolean): Boolean? =
when (this) {
is IrConst<*> -> value == null
is IrGetValue -> when {
symbol == knownVariableSymbol -> knownVariableIsNull
!type.isNullable() -> false
else -> null
}
is IrConst<*> ->
value == null
is IrGetValue ->
when {
symbol == knownVariableSymbol -> knownVariableIsNull
!type.isNullable() -> false
else -> null
}
is IrConstructorCall,
is IrGetSingletonValue,
is IrFunctionExpression,
is IrCallableReference<*>,
is IrClassReference,
is IrGetClass -> false
is IrGetClass ->
false
is IrCall ->
if (!type.isNullable() && symbol.owner.isStable()) false else null
if (!type.isNullable()) false else null
is IrGetField ->
if (!type.isNullable() && symbol.owner.isStable()) false else null
is IrBlock ->
(statements.singleOrNull() as IrExpression?)?.takeIf { it.type == type }?.isNull(knownVariableSymbol, knownVariableIsNull)
if (!type.isNullable()) false else null
is IrBlock -> {
val singleExpr = statements.singleOrNull()
if (singleExpr is IrExpression && singleExpr.type == type)
singleExpr.isNull(knownVariableSymbol, knownVariableIsNull)
else
null
}
else -> null
}
// TODO make calls to the declarations within the same module "stable"
private fun IrDeclaration.isStable() =
fileOrNull == currentFile
}
private class IfNullExpr(
@@ -0,0 +1,50 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: safeCallWithElvisAndEnhancedNullability.kt
import kotlin.test.assertEquals
fun check(a : A?) : Int {
return a?.y?.x ?: (a?.x ?: 3)
}
fun checkLeftAssoc(a : A?) : Int {
return (a?.y?.x ?: a?.x) ?: 3
}
fun box() : String {
val a1 = A(2, A(1, null))
val a2 = A(2, null)
val a3 = null
assertEquals(1, check(a1))
assertEquals(2, check(a2))
assertEquals(3, check(a3))
assertEquals(1, checkLeftAssoc(a1))
assertEquals(2, checkLeftAssoc(a2))
assertEquals(3, checkLeftAssoc(a3))
return "OK"
}
// FILE: A.java
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class A {
private Integer x;
private A y;
public A(Integer x, A y) {
this.x = x;
this.y = y;
}
@NotNull
public Integer getX() { return x; }
@Nullable
public A getY() { return y; }
}
@@ -0,0 +1,33 @@
// WITH_RUNTIME
// MODULE: lib
// FILE: a.kt
class A(val x : Int, val y : A?)
// MODULE: main(lib)
// FILE: safeCallWithElvisMultipleFiles.kt
import kotlin.test.assertEquals
fun check(a : A?) : Int {
return a?.y?.x ?: (a?.x ?: 3)
}
fun checkLeftAssoc(a : A?) : Int {
return (a?.y?.x ?: a?.x) ?: 3
}
fun box() : String {
val a1 = A(2, A(1, null))
val a2 = A(2, null)
val a3 = null
assertEquals(1, check(a1))
assertEquals(2, check(a2))
assertEquals(3, check(a3))
assertEquals(1, checkLeftAssoc(a1))
assertEquals(2, checkLeftAssoc(a2))
assertEquals(3, checkLeftAssoc(a3))
return "OK"
}
@@ -0,0 +1,18 @@
// MODULE: lib
// FILE: a.kt
class A(val x : Int, val y : A?)
// MODULE: main(lib)
// FILE: main.kt
fun check(a : A?) : Int {
return a?.y?.x ?: (a?.x ?: 3)
}
// 0 valueOf
// 0 Value\s\(\)
// JVM_TEMPLATES:
// 0 ACONST_NULL
// JVM_IR_TEMPLATES:
// 1 ACONST_NULL
@@ -1760,6 +1760,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisAndEnhancedNullability.kt")
public void testSafeCallWithElvisAndEnhancedNullability() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -724,6 +724,12 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("severalInlines.kt")
public void testSeveralInlines() throws Exception {
@@ -1850,6 +1850,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisAndEnhancedNullability.kt")
public void testSafeCallWithElvisAndEnhancedNullability() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
@@ -730,6 +730,12 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvis.kt");
}
@Test
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/bytecodeText/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@Test
@TestMetadata("severalInlines.kt")
public void testSeveralInlines() throws Exception {
@@ -1555,6 +1555,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@TestMetadata("safeCallWithElvisAndEnhancedNullability.kt")
public void testSafeCallWithElvisAndEnhancedNullability() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisAndEnhancedNullability.kt");
}
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");
@@ -1030,6 +1030,11 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");
@@ -1030,6 +1030,11 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");
@@ -1030,6 +1030,11 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");
@@ -900,6 +900,11 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvis.kt");
}
@TestMetadata("safeCallWithElvisMultipleFiles.kt")
public void testSafeCallWithElvisMultipleFiles() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/safeCallWithElvisMultipleFiles.kt");
}
@TestMetadata("simple.kt")
public void testSimple() throws Exception {
runTest("compiler/testData/codegen/box/boxingOptimization/simple.kt");