Optimize ir interpretation of Enum.name
There is no need to create honest object in case of such simple calculation. Furthermore, it can be harmful if enum class has non-constant initializer or property. #KT-53480 Fixed
This commit is contained in:
+6
@@ -8133,6 +8133,12 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT
|
|||||||
runTest("compiler/testData/codegen/box/constants/foldingBinaryOpsUnsignedConst.kt");
|
runTest("compiler/testData/codegen/box/constants/foldingBinaryOpsUnsignedConst.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt53480.kt")
|
||||||
|
public void testKt53480() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/constants/kt53480.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt9532.kt")
|
@TestMetadata("kt9532.kt")
|
||||||
public void testKt9532() throws Exception {
|
public void testKt9532() throws Exception {
|
||||||
|
|||||||
+15
-2
@@ -90,7 +90,8 @@ private fun unfoldFunction(function: IrSimpleFunction, environment: IrInterprete
|
|||||||
|
|
||||||
private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) {
|
private fun unfoldConstructor(constructor: IrConstructor, callStack: CallStack) {
|
||||||
when (constructor.fqName) {
|
when (constructor.fqName) {
|
||||||
"kotlin.Enum.<init>", "kotlin.Throwable.<init>" -> {
|
"kotlin.Enum.<init>" -> return // all properties already initialized in `interpretEnumEntry`
|
||||||
|
"kotlin.Throwable.<init>" -> {
|
||||||
val irClass = constructor.parentAsClass
|
val irClass = constructor.parentAsClass
|
||||||
val receiverSymbol = irClass.thisReceiver!!.symbol
|
val receiverSymbol = irClass.thisReceiver!!.symbol
|
||||||
val receiverState = callStack.loadState(receiverSymbol)
|
val receiverState = callStack.loadState(receiverSymbol)
|
||||||
@@ -196,11 +197,15 @@ private fun unfoldEnumConstructorCall(element: IrEnumConstructorCall, environmen
|
|||||||
private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) {
|
private fun unfoldInstanceInitializerCall(instanceInitializerCall: IrInstanceInitializerCall, callStack: CallStack) {
|
||||||
val irClass = instanceInitializerCall.classSymbol.owner
|
val irClass = instanceInitializerCall.classSymbol.owner
|
||||||
val toInitialize = irClass.declarations.filter { it is IrProperty || it is IrAnonymousInitializer }
|
val toInitialize = irClass.declarations.filter { it is IrProperty || it is IrAnonymousInitializer }
|
||||||
|
val state = irClass.thisReceiver?.symbol?.let { callStack.loadState(it) } // try to avoid recalculation of properties
|
||||||
|
|
||||||
toInitialize.reversed().forEach {
|
toInitialize.reversed().forEach {
|
||||||
when {
|
when {
|
||||||
it is IrAnonymousInitializer -> callStack.pushCompoundInstruction(it.body)
|
it is IrAnonymousInitializer -> callStack.pushCompoundInstruction(it.body)
|
||||||
it is IrProperty && it.backingField?.initializer?.expression != null -> callStack.pushCompoundInstruction(it.backingField)
|
|
||||||
|
it is IrProperty && it.backingField?.initializer?.expression != null && state?.getField(it.symbol) == null -> {
|
||||||
|
callStack.pushCompoundInstruction(it.backingField)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -272,6 +277,14 @@ private fun unfoldGetEnumValue(expression: IrGetEnumValue, environment: IrInterp
|
|||||||
val callStack = environment.callStack
|
val callStack = environment.callStack
|
||||||
environment.mapOfEnums[expression.symbol]?.let { return callStack.pushState(it) }
|
environment.mapOfEnums[expression.symbol]?.let { return callStack.pushState(it) }
|
||||||
|
|
||||||
|
val frameOwner = callStack.currentFrameOwner
|
||||||
|
if (frameOwner is IrCall && frameOwner.dispatchReceiver is IrGetEnumValue && frameOwner.symbol.owner.name.asString() == "<get-name>") {
|
||||||
|
// this optimization allow us to avoid creation of enum object when we try to interpret simple `name` call; see KT-53480
|
||||||
|
val enumEntry = (frameOwner.dispatchReceiver as IrGetEnumValue).symbol.owner
|
||||||
|
callStack.pushState(enumEntry.toState(environment.irBuiltIns))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
callStack.pushSimpleInstruction(expression)
|
callStack.pushSimpleInstruction(expression)
|
||||||
val enumEntry = expression.symbol.owner
|
val enumEntry = expression.symbol.owner
|
||||||
val enumClass = enumEntry.symbol.owner.parentAsClass
|
val enumClass = enumEntry.symbol.owner.parentAsClass
|
||||||
|
|||||||
+3
-18
@@ -206,7 +206,6 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
|||||||
if (callStack.peekState() == null) callStack.pushState(getUnitState()) // implicit Unit result
|
if (callStack.peekState() == null) callStack.pushState(getUnitState()) // implicit Unit result
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("UNUSED_PARAMETER")
|
|
||||||
private fun interpretConstructor(constructor: IrConstructor) {
|
private fun interpretConstructor(constructor: IrConstructor) {
|
||||||
callStack.pushState(callStack.loadState(constructor.parentAsClass.thisReceiver!!.symbol))
|
callStack.pushState(callStack.loadState(constructor.parentAsClass.thisReceiver!!.symbol))
|
||||||
callStack.dropFrameAndCopyResult()
|
callStack.dropFrameAndCopyResult()
|
||||||
@@ -390,28 +389,14 @@ class IrInterpreter(internal val environment: IrInterpreterEnvironment, internal
|
|||||||
|
|
||||||
private fun interpretEnumEntry(enumEntry: IrEnumEntry) {
|
private fun interpretEnumEntry(enumEntry: IrEnumEntry) {
|
||||||
callInterceptor.interceptEnumEntry(enumEntry) {
|
callInterceptor.interceptEnumEntry(enumEntry) {
|
||||||
val enumClass = enumEntry.symbol.owner.parentAsClass
|
val enumClassObject = enumEntry.toState(irBuiltIns)
|
||||||
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
|
environment.mapOfEnums[enumEntry.symbol] = enumClassObject
|
||||||
|
|
||||||
val enumInitializer = enumEntry.initializerExpression?.expression
|
val enumInitializer = enumEntry.initializerExpression?.expression
|
||||||
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqName} is null")
|
?: throw InterpreterError("Initializer at enum entry ${enumEntry.fqName} is null")
|
||||||
val enumConstructorCall = enumInitializer as? IrEnumConstructorCall
|
val enumConstructorCall = enumInitializer as? IrEnumConstructorCall
|
||||||
?: (enumInitializer as IrBlock).statements.filterIsInstance<IrEnumConstructorCall>().single()
|
?: (enumInitializer as IrBlock).statements.filterIsInstance<IrEnumConstructorCall>().single()
|
||||||
|
|
||||||
val enumClassObject = Common(enumEntry.correspondingClass ?: enumClass)
|
|
||||||
environment.mapOfEnums[enumEntry.symbol] = enumClassObject
|
|
||||||
|
|
||||||
if (enumEntries.isNotEmpty()) {
|
|
||||||
// these fields will be evaluated during interpretation of constructor call
|
|
||||||
// but we need them right away to create correct call to Enum's constructor
|
|
||||||
val valueArguments = listOf(
|
|
||||||
Primitive(enumEntry.name.asString(), irBuiltIns.stringType),
|
|
||||||
Primitive(enumEntries.indexOf(enumEntry), irBuiltIns.intType)
|
|
||||||
)
|
|
||||||
irBuiltIns.enumClass.owner.declarations.filterIsInstance<IrProperty>().zip(valueArguments).forEach { (property, argument) ->
|
|
||||||
enumClassObject.setField(property.symbol, argument)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callStack.newSubFrame(enumEntry)
|
callStack.newSubFrame(enumEntry)
|
||||||
callStack.pushCompoundInstruction(enumEntry) // not really a compound instruction
|
callStack.pushCompoundInstruction(enumEntry) // not really a compound instruction
|
||||||
callStack.pushCompoundInstruction(enumInitializer)
|
callStack.pushCompoundInstruction(enumInitializer)
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.builtins.PrimitiveType
|
|||||||
import org.jetbrains.kotlin.builtins.StandardNames
|
import org.jetbrains.kotlin.builtins.StandardNames
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.descriptors.Modality
|
import org.jetbrains.kotlin.descriptors.Modality
|
||||||
|
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.IrElement
|
import org.jetbrains.kotlin.ir.IrElement
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.*
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
@@ -312,3 +313,21 @@ internal fun State.unsignedToString(): String {
|
|||||||
else -> (value as Number).toLong().toULong().toString()
|
else -> (value as Number).toLong().toULong().toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun IrEnumEntry.toState(irBuiltIns: IrBuiltIns): Common {
|
||||||
|
val enumClass = this.symbol.owner.parentAsClass
|
||||||
|
val enumEntries = enumClass.declarations.filterIsInstance<IrEnumEntry>()
|
||||||
|
val enumClassObject = Common(this.correspondingClass ?: enumClass)
|
||||||
|
|
||||||
|
if (enumEntries.isNotEmpty()) {
|
||||||
|
val valueArguments = listOf(
|
||||||
|
Primitive(this.name.asString(), irBuiltIns.stringType),
|
||||||
|
Primitive(enumEntries.indexOf(this), irBuiltIns.intType)
|
||||||
|
)
|
||||||
|
irBuiltIns.enumClass.owner.declarations.filterIsInstance<IrProperty>().zip(valueArguments).forEach { (property, argument) ->
|
||||||
|
enumClassObject.setField(property.symbol, argument)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enumClassObject
|
||||||
|
}
|
||||||
|
|||||||
-1
@@ -11,7 +11,6 @@ import org.jetbrains.kotlin.ir.interpreter.IrInterpreterEnvironment
|
|||||||
import org.jetbrains.kotlin.ir.interpreter.fqName
|
import org.jetbrains.kotlin.ir.interpreter.fqName
|
||||||
|
|
||||||
internal object IntrinsicEvaluator {
|
internal object IntrinsicEvaluator {
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
|
||||||
private val fqNameToHandler: Map<String, IntrinsicBase> = buildMap {
|
private val fqNameToHandler: Map<String, IntrinsicBase> = buildMap {
|
||||||
listOf(
|
listOf(
|
||||||
EmptyArray, ArrayOf, ArrayOfNulls, ArrayConstructor, EnumValues, EnumValueOf,
|
EmptyArray, ArrayOf, ArrayOfNulls, ArrayConstructor, EnumValues, EnumValueOf,
|
||||||
|
|||||||
+7
-10
@@ -7,9 +7,6 @@ package org.jetbrains.kotlin.ir.interpreter.intrinsics
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.interpreter.*
|
import org.jetbrains.kotlin.ir.interpreter.*
|
||||||
import org.jetbrains.kotlin.ir.interpreter.createCall
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.createGetValue
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.toIrConst
|
|
||||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
|
import org.jetbrains.kotlin.ir.interpreter.exceptions.handleUserException
|
||||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.stop
|
import org.jetbrains.kotlin.ir.interpreter.exceptions.stop
|
||||||
import org.jetbrains.kotlin.ir.interpreter.exceptions.withExceptionHandler
|
import org.jetbrains.kotlin.ir.interpreter.exceptions.withExceptionHandler
|
||||||
@@ -30,7 +27,7 @@ internal sealed class IntrinsicBase {
|
|||||||
return listOf(customEvaluateInstruction(irFunction, environment))
|
return listOf(customEvaluateInstruction(irFunction, environment))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun customEvaluateInstruction(irFunction: IrFunction, environment: IrInterpreterEnvironment): CustomInstruction {
|
private fun customEvaluateInstruction(irFunction: IrFunction, environment: IrInterpreterEnvironment): CustomInstruction {
|
||||||
return CustomInstruction {
|
return CustomInstruction {
|
||||||
withExceptionHandler(environment) { // Exception handling is used only for indent actions; TODO: drop later
|
withExceptionHandler(environment) { // Exception handling is used only for indent actions; TODO: drop later
|
||||||
evaluate(irFunction, environment)
|
evaluate(irFunction, environment)
|
||||||
@@ -140,7 +137,7 @@ internal object EnumValueOf : IntrinsicBase() {
|
|||||||
|
|
||||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
val enumEntry = getEnumEntryByName(irFunction, environment) ?: return emptyList()
|
val enumEntry = getEnumEntryByName(irFunction, environment) ?: return emptyList()
|
||||||
return listOf(customEvaluateInstruction(irFunction, environment), SimpleInstruction(enumEntry))
|
return super.unwind(irFunction, environment) + SimpleInstruction(enumEntry)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||||
@@ -158,7 +155,7 @@ internal object EnumIntrinsics : IntrinsicBase() {
|
|||||||
|
|
||||||
fun canHandleFunctionWithName(fqName: String, origin: IrDeclarationOrigin): Boolean {
|
fun canHandleFunctionWithName(fqName: String, origin: IrDeclarationOrigin): Boolean {
|
||||||
if (origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER) return true
|
if (origin == IrDeclarationOrigin.ENUM_CLASS_SPECIAL_MEMBER) return true
|
||||||
return fqName.startsWith("kotlin.Enum.")
|
return fqName.startsWith("kotlin.Enum.") && fqName != "kotlin.Enum.<init>"
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
@@ -229,9 +226,9 @@ internal object ArrayConstructor : IntrinsicBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
|
if (irFunction.valueParameters.size == 1) return super.unwind(irFunction, environment)
|
||||||
val callStack = environment.callStack
|
val callStack = environment.callStack
|
||||||
val instructions = mutableListOf<Instruction>(customEvaluateInstruction(irFunction, environment))
|
val instructions = super.unwind(irFunction, environment).toMutableList()
|
||||||
|
|
||||||
val sizeSymbol = irFunction.valueParameters[0].symbol
|
val sizeSymbol = irFunction.valueParameters[0].symbol
|
||||||
val size = callStack.loadState(sizeSymbol).asInt()
|
val size = callStack.loadState(sizeSymbol).asInt()
|
||||||
@@ -298,14 +295,14 @@ internal object AssertIntrinsic : IntrinsicBase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
override fun unwind(irFunction: IrFunction, environment: IrInterpreterEnvironment): List<Instruction> {
|
||||||
if (irFunction.valueParameters.size == 1) return listOf(customEvaluateInstruction(irFunction, environment))
|
if (irFunction.valueParameters.size == 1) return super.unwind(irFunction, environment)
|
||||||
|
|
||||||
val lambdaParameter = irFunction.valueParameters.last()
|
val lambdaParameter = irFunction.valueParameters.last()
|
||||||
val lambdaState = environment.callStack.loadState(lambdaParameter.symbol) as KFunctionState
|
val lambdaState = environment.callStack.loadState(lambdaParameter.symbol) as KFunctionState
|
||||||
val call = (lambdaState.invokeSymbol.owner as IrSimpleFunction).createCall()
|
val call = (lambdaState.invokeSymbol.owner as IrSimpleFunction).createCall()
|
||||||
call.dispatchReceiver = lambdaParameter.createGetValue()
|
call.dispatchReceiver = lambdaParameter.createGetValue()
|
||||||
|
|
||||||
return listOf(customEvaluateInstruction(irFunction, environment), CompoundInstruction(call))
|
return super.unwind(irFunction, environment) + CompoundInstruction(call)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
override fun evaluate(irFunction: IrFunction, environment: IrInterpreterEnvironment) {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
// TARGET_BACKEND: JVM_IR
|
||||||
|
// FILE: J.java
|
||||||
|
public class J {
|
||||||
|
public static int f() { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: Main.kt
|
||||||
|
enum class A {
|
||||||
|
OK;
|
||||||
|
val x = J.f()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
return A.OK.name
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// FILE: J.java
|
||||||
|
public class J {
|
||||||
|
public static int f() { return 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// FILE: Main.kt
|
||||||
|
enum class A {
|
||||||
|
E;
|
||||||
|
val x = J.f()
|
||||||
|
}
|
||||||
|
|
||||||
|
const val name = A.E.name
|
||||||
+6
@@ -8133,6 +8133,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
|
|||||||
runTest("compiler/testData/codegen/box/constants/foldingBinaryOpsUnsignedConst.kt");
|
runTest("compiler/testData/codegen/box/constants/foldingBinaryOpsUnsignedConst.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt53480.kt")
|
||||||
|
public void testKt53480() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/box/constants/kt53480.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("kt9532.kt")
|
@TestMetadata("kt9532.kt")
|
||||||
public void testKt9532() throws Exception {
|
public void testKt9532() throws Exception {
|
||||||
|
|||||||
+6
@@ -205,6 +205,12 @@ public class IrInterpreterAfterFir2IrTestGenerated extends AbstractIrInterpreter
|
|||||||
runTest("compiler/testData/ir/interpreter/interfaceDefault.kt");
|
runTest("compiler/testData/ir/interpreter/interfaceDefault.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt53480.kt")
|
||||||
|
public void testKt53480() throws Exception {
|
||||||
|
runTest("compiler/testData/ir/interpreter/kt53480.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("lambda.kt")
|
@TestMetadata("lambda.kt")
|
||||||
public void testLambda() throws Exception {
|
public void testLambda() throws Exception {
|
||||||
|
|||||||
+6
@@ -205,6 +205,12 @@ public class IrInterpreterAfterPsi2IrTestGenerated extends AbstractIrInterpreter
|
|||||||
runTest("compiler/testData/ir/interpreter/interfaceDefault.kt");
|
runTest("compiler/testData/ir/interpreter/interfaceDefault.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kt53480.kt")
|
||||||
|
public void testKt53480() throws Exception {
|
||||||
|
runTest("compiler/testData/ir/interpreter/kt53480.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("lambda.kt")
|
@TestMetadata("lambda.kt")
|
||||||
public void testLambda() throws Exception {
|
public void testLambda() throws Exception {
|
||||||
|
|||||||
Reference in New Issue
Block a user