[JS IR] Optimize away upcasts

#KT-50212 Fixed
This commit is contained in:
Sergej Jaskiewicz
2021-12-20 18:33:47 +03:00
committed by Space
parent 400a5a0f34
commit 8149189585
7 changed files with 217 additions and 10 deletions
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.BodyLoweringPass
import org.jetbrains.kotlin.backend.common.compilationException
import org.jetbrains.kotlin.backend.common.ir.isPure
import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
import org.jetbrains.kotlin.ir.backend.js.JsIrBackendContext
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrArithBuilder
import org.jetbrains.kotlin.ir.backend.js.ir.JsIrBuilder
@@ -18,6 +19,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.declarations.IrDeclarationParent
import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrConstImpl
import org.jetbrains.kotlin.ir.expressions.impl.IrTypeOperatorCallImpl
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
@@ -211,12 +213,53 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
}
}
/**
* The general logic for generating a runtime type check is as follows:
* ```
* ┌─────────────────────────────┬───────────────────────────────┐
* │ to non-null │ to nullable │
* ┌─────────────╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
* │ ┃ instanceof check │
* │from non-null┃ OR │
* │ ┃ advanced check │
* ├─────────────╋─────────────────────────────┬───────────────────────────────┤
* │ ┃ instanceof check │ null check + instanceof check │
* │from nullable┃ OR │ OR │
* │ ┃ null check + advanced check │ null check + advanced check │
* └─────────────┻─────────────────────────────┴───────────────────────────────┘
* ```
* Note: advanced check is performed when casting to primitive types, array types, function types, or interfaces.
*
* If we statically know that this is an upcast, we try to generate no checks.
* In this case, the following logic is applied:
* ```
* ┌─────────────┬─────────────┐
* │ to non-null │ to nullable │
* ┌─────────────╋━━━━━━━━━━━━━┻━━━━━━━━━━━━━┫
* │from non-null┃ true │
* ├─────────────╋─────────────┬─────────────┤
* │from nullable┃ null check │ true │
* └─────────────┻─────────────┴─────────────┘
* ```
*/
private fun generateTypeCheck(argument: () -> IrExpression, toType: IrType): IrExpression {
val toNotNullable = toType.makeNotNull()
val argumentInstance = argument()
val instanceCheck = generateTypeCheckNonNull(argumentInstance, toNotNullable)
val isFromNullable = argumentInstance.type.isNullable()
val fromType = argumentInstance.type
val fromNotNullable = fromType.makeNotNull()
val isFromNullable = fromType.isNullable()
val isToNullable = toType.isNullable()
if (fromNotNullable !is IrDynamicType && fromNotNullable.isSubtypeOf(toNotNullable, context.typeSystem)) {
// This is an upcast
return when {
isFromNullable && !isToNullable -> calculator.run { not(nullCheck(argument())) }
else -> IrConstImpl.constTrue(UNDEFINED_OFFSET, UNDEFINED_OFFSET, context.irBuiltIns.booleanType)
}
}
val instanceCheck = generateTypeCheckNonNull(argumentInstance, toNotNullable)
val isNativeCheck = !advancedCheckRequired(toNotNullable)
return when {
+3 -3
View File
@@ -4,9 +4,9 @@
// MODULE: lib
// FILE: t.kt
fun bar(a: String, b: String): String
fun bar(a: String, b: String): Any
fun foo(): String {
fun foo(): Any {
return bar("O", "K")
}
@@ -17,4 +17,4 @@ fun box(): String {
val r = foo()
if (r is String) return r
return "OK"
}
}