From 0ea8fc5b082d26230b770fd1fced3b7b3c53b8ec Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Wed, 10 Aug 2022 18:36:36 +0300 Subject: [PATCH] [K/N][IR] Refactored enums lowering --- .../jetbrains/kotlin/backend/konan/Context.kt | 8 +- .../kotlin/backend/konan/ToplevelPhases.kt | 35 ++ .../backend/konan/llvm/CodeGenerator.kt | 15 +- .../backend/konan/lower/EnumClassLowering.kt | 288 +++++++--------- .../konan/lower/EnumConstructorsLowering.kt | 2 +- .../lower/EnumSpecialDeclarationsFactory.kt | 309 +++++------------- 6 files changed, 241 insertions(+), 416 deletions(-) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index f456f601d89..f60286cecd9 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -18,7 +18,6 @@ import org.jetbrains.kotlin.backend.konan.lower.BridgesSupport import org.jetbrains.kotlin.backend.konan.lower.EnumsSupport import org.jetbrains.kotlin.backend.konan.lower.InlineFunctionsSupport import org.jetbrains.kotlin.backend.konan.lower.InnerClassesSupport -import org.jetbrains.kotlin.backend.konan.lower.InternalLoweredEnum import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG @@ -60,11 +59,12 @@ internal class NativeMapping : DefaultMapping() { data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections) val outerThisFields = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + val enumImplObjects = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping() + val enumEntriesMaps = mutableMapOf>() val bridges = mutableMapOf() val notLoweredInlineFunctions = mutableMapOf() val loweredInlineFunctions = mutableMapOf() - val internalLoweredEnums = mutableMapOf() - val externalLoweredEnums = mutableMapOf() } internal class Context(config: KonanConfig) : KonanBackendContext(config) { @@ -104,7 +104,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) } val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) } val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) } - val enumsSupport by lazy { EnumsSupport(this) } + val enumsSupport by lazy { EnumsSupport(mapping, ir.symbols, irBuiltIns, irFactory) } open class LazyMember(val initializer: Context.() -> T) { operator fun getValue(thisRef: Context, property: KProperty<*>): T = thisRef.getValue(this) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index f801a7b3a17..c64c8306119 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMo import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder +import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_ENUM import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.lower.SamSuperTypesChecker import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl +import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -427,6 +429,21 @@ internal val exportInternalAbiPhase = makeKonanModuleOpPhase( } context.internalAbi.declare(function, declaration.module) } + + if (declaration.isEnumClass) { + val function = context.irFactory.buildFun { + name = InternalAbi.getEnumValuesAccessorName(declaration) + returnType = context.ir.symbols.array.typeWith(declaration.defaultType) + origin = InternalAbi.INTERNAL_ABI_ORIGIN + } + + context.createIrBuilder(function.symbol).run { + function.body = irBlockBody { + +irReturn(with(context.enumsSupport) { irGetValuesField(declaration) }) + } + } + context.internalAbi.declare(function, declaration.module) + } } override fun visitProperty(declaration: IrProperty) { @@ -470,6 +487,7 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase( val companionObjectAccessors = mutableMapOf() val outerThisAccessors = mutableMapOf() val lateinitPropertyAccessors = mutableMapOf() + val enumValuesAccessors = mutableMapOf() val transformer = object : IrElementTransformerVoid() { override fun visitGetObjectValue(expression: IrGetObjectValue): IrExpression { @@ -561,6 +579,23 @@ internal val useInternalAbiPhase = makeKonanModuleOpPhase( } } + field.origin == DECLARATION_ORIGIN_ENUM -> { + val enumClass = irClass?.parentClassOrNull + require(enumClass != null) { "Unexpected usage of enum VALUES field" } + require(enumClass.isEnumClass) { "Expected a enum class: ${enumClass.render()}" } + val accessor = enumValuesAccessors.getOrPut(enumClass) { + context.irFactory.buildFun { + name = InternalAbi.getEnumValuesAccessorName(enumClass) + returnType = context.ir.symbols.array.typeWith(enumClass.defaultType) + origin = InternalAbi.INTERNAL_ABI_ORIGIN + isExternal = true + }.also { + context.internalAbi.reference(it, enumClass.module) + } + } + return IrCallImpl(expression.startOffset, expression.endOffset, expression.type, accessor.symbol, accessor.typeParameters.size, accessor.valueParameters.size) + } + else -> expression } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 50518c23e62..bcfd8d68e0b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -1366,19 +1366,10 @@ internal abstract class FunctionGenerationContext( */ fun getEnumEntry(enumEntry: IrEnumEntry, exceptionHandler: ExceptionHandler): LLVMValueRef { val enumClass = enumEntry.parentAsClass - val loweredEnum = context.enumsSupport.getLoweredEnum(enumClass) - - val getterId = loweredEnum.entriesMap[enumEntry.name]!!.getterId - val values = call( - loweredEnum.valuesGetter.llvmFunction.llvmValue, - emptyList(), - Lifetime.ARGUMENT, - exceptionHandler - ) - + val getterId = context.enumsSupport.enumEntriesMap(enumClass)[enumEntry.name]!!.getterId return call( - loweredEnum.itemGetterSymbol.owner.llvmFunction.llvmValue, - listOf(values, Int32(getterId).llvm), + context.enumsSupport.getValueGetter(enumClass).llvmFunction.llvmValue, + listOf(Int32(getterId).llvm), Lifetime.GLOBAL, exceptionHandler ) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index e7aadc60872..a1953e396d1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -18,53 +18,27 @@ import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrPropertyImpl import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin.ARGUMENTS_REORDERING_FOR_CALL import org.jetbrains.kotlin.ir.symbols.IrClassSymbol -import org.jetbrains.kotlin.ir.symbols.impl.IrPropertySymbolImpl import org.jetbrains.kotlin.ir.types.classifierOrNull -import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementTransformer import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name -private class EnumSyntheticFunctionsBuilder(val context: Context) { - fun IrBuilderWithScope.enumValues(enumClass: IrClass): IrExpression { - val loweredEnum = this@EnumSyntheticFunctionsBuilder.context.enumsSupport.getLoweredEnum(enumClass) - return irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply { - putValueArgument(0, loweredEnum.getValuesField(startOffset, endOffset)) - } - } - - fun IrBuilderWithScope.enumValueOf(enumClass: IrClass, value: IrExpression): IrExpression { - val loweredEnum = this@EnumSyntheticFunctionsBuilder.context.enumsSupport.getLoweredEnum(enumClass) - return irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply { - putValueArgument(0, value) - putValueArgument(1, loweredEnum.getValuesField(startOffset, endOffset)) - } - } - - private val genericValueOfSymbol = context.ir.symbols.valueOfForEnum - - private val genericValuesSymbol = context.ir.symbols.valuesForEnum -} - internal class NativeEnumWhenLowering constructor(context: Context) : EnumWhenLowering(context) { override fun mapConstEnumEntry(entry: IrEnumEntry): Int { - val parent = entry.parentAsClass - val loweredEnum = (context as Context).enumsSupport.getLoweredEnumOrNull(parent) - ?: return super.mapConstEnumEntry(entry) - return loweredEnum.entriesMap[entry.name]!!.ordinal + val enumEntriesMap = (context as Context).enumsSupport.enumEntriesMap(entry.parentAsClass) + return enumEntriesMap[entry.name]!!.ordinal } } -internal class EnumUsageLowering(val context: Context) - : IrElementTransformer, FileLoweringPass { - - private val enumSyntheticFunctionsBuilder = EnumSyntheticFunctionsBuilder(context) +internal class EnumUsageLowering(val context: Context) : IrElementTransformer, FileLoweringPass { + private val enumsSupport = context.enumsSupport + private val symbols = context.ir.symbols + private val arrayGet = symbols.arrayGet[symbols.array]!! override fun lower(irFile: IrFile) { visitFile(irFile, data = null) @@ -99,25 +73,28 @@ internal class EnumUsageLowering(val context: Context) require(irClass.kind == ClassKind.ENUM_CLASS) - return with(enumSyntheticFunctionsBuilder) { + return with(enumsSupport) { if (intrinsicType == IntrinsicType.ENUM_VALUES) - data.enumValues(irClass) + data.irEnumValues(irClass) else - data.enumValueOf(irClass, expression.getValueArgument(0)!!) + data.irEnumValueOf(irClass, expression.getValueArgument(0)!!) } } - private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name): IrExpression { - val loweredEnum = this@EnumUsageLowering.context.enumsSupport.getLoweredEnum(enumClass) - val getterId = loweredEnum.entriesMap.getValue(name).getterId - return irCall(loweredEnum.itemGetterSymbol, enumClass.defaultType).apply { - dispatchReceiver = irCall(loweredEnum.valuesGetter) - putValueArgument(0, irInt(getterId)) - } - } + private fun IrBuilderWithScope.loadEnumEntry(enumClass: IrClass, name: Name) = + irCall(arrayGet, enumClass.defaultType).apply { + dispatchReceiver = with(enumsSupport) { irGetValuesField(enumClass) } + putValueArgument(0, irInt(enumsSupport.enumEntriesMap(enumClass).getValue(name).getterId)) + } } internal class EnumClassLowering(val context: Context) : FileLoweringPass { + private val enumsSupport = context.enumsSupport + private val symbols = context.ir.symbols + private val createUninitializedInstance = symbols.createUninitializedInstance + private val initInstance = symbols.initInstance + private val arrayGet = symbols.arrayGet[symbols.array]!! + private val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first() override fun lower(irFile: IrFile) { irFile.transformChildrenVoid(object : IrElementTransformerVoid() { @@ -131,167 +108,137 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { } private inner class EnumClassTransformer(val irClass: IrClass) { - private val loweredEnum = context.enumsSupport.getInternalLoweredEnum(irClass) - private val enumSyntheticFunctionsBuilder = EnumSyntheticFunctionsBuilder(context) + private val implObject = enumsSupport.getImplObject(irClass) + private val valuesField = enumsSupport.getValuesField(implObject) + private val enumEntriesMap = enumsSupport.enumEntriesMap(irClass) fun run() { - pullUpEnumEntriesClasses() - createImplObject() + val enumEntries = transformEnumBody() + defineImplObject(enumEntries) + defineValueGetter() } - private fun pullUpEnumEntriesClasses() { - irClass.declarations.transformFlat { declaration -> - if (declaration is IrEnumEntry) { - val correspondingClass = declaration.correspondingClass - declaration.correspondingClass = null - listOfNotNull(declaration, correspondingClass) - } else null - } - } - - private fun createImplObject() { - val implObject = loweredEnum.implObject - + private fun transformEnumBody(): List { val enumEntries = mutableListOf() - var i = 0 - while (i < irClass.declarations.size) { - val declaration = irClass.declarations[i] - var delete = false + irClass.declarations.transformFlat { declaration -> when (declaration) { is IrEnumEntry -> { enumEntries.add(declaration) - delete = true + val correspondingClass = declaration.correspondingClass + declaration.correspondingClass = null + listOfNotNull(correspondingClass) } + is IrFunction -> { val body = declaration.body if (body is IrSyntheticBody) { - when (body.kind) { - IrSyntheticBodyKind.ENUM_VALUEOF -> - declaration.body = createSyntheticValueOfMethodBody(declaration) - IrSyntheticBodyKind.ENUM_VALUES -> - declaration.body = createSyntheticValuesMethodBody(declaration) + declaration.body = when (body.kind) { + IrSyntheticBodyKind.ENUM_VALUEOF -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) { + +irReturn(with(enumsSupport) { irEnumValueOf(irClass, irGet(declaration.valueParameters[0])) }) + } + + IrSyntheticBodyKind.ENUM_VALUES -> context.createIrBuilder(declaration.symbol).irBlockBody(declaration) { + +irReturn(with(enumsSupport) { irEnumValues(irClass) }) + } + IrSyntheticBodyKind.ENUM_ENTRIES -> TODO("KT-48872 is not yet supported") } } + null } + + else -> null } - if (delete) - irClass.declarations.removeAt(i) - else - ++i } + return enumEntries + } - implObject.declarations += createSyntheticValuesPropertyDeclaration(enumEntries) + private fun defineValueGetter() { + val valueGetter = enumsSupport.getValueGetter(irClass) + context.createIrBuilder(valueGetter.symbol).run { + valueGetter.body = irBlockBody(valueGetter) { + +irReturn(irCall(arrayGet, irClass.defaultType).apply { + dispatchReceiver = with(enumsSupport) { irGetValuesField(irClass) } + putValueArgument(0, irGet(valueGetter.valueParameters[0])) + }) + } + } + irClass.declarations.add(valueGetter) + } + + private fun defineImplObject(enumEntries: List) { + implObject.createParameterDeclarations() + + implObject.addSimpleDelegatingConstructor(constructorOfAny, context.irBuiltIns, true /* TODO: why primary? */) + implObject.addFakeOverrides(context.typeSystem) + irClass.declarations.add(implObject) + + ImplConstructorBuilder(implObject.constructors.single()).build(enumEntries) + } + + private inner class ImplConstructorBuilder(val constructor: IrConstructor) { + private val irBuilder = context.createIrBuilder(constructor.symbol, constructor.startOffset, constructor.endOffset) + + fun build(enumEntries: List) { + val statements = (constructor.body as IrBlockBody).statements - val companion = irClass.companionObject() - if (companion != null) { // The initialization order is the following: // - first all enum entries in their declaration order // - then companion object if it exists - val constructor = implObject.constructors.single() - context.createIrBuilder(constructor.symbol).run { - (constructor.body as IrBlockBody).statements += irGetObject(companion.symbol) - } + statements += buildValuesFieldInitializer(enumEntries) + // Split allocation and constructors calling because enum entries can reference one another. + statements += callEnumEntriesConstructors(enumEntries) + + irClass.companionObject()?.let { statements += irBuilder.irGetObject(it.symbol) } } - irClass.declarations += implObject - } - - private val createUninitializedInstance = context.ir.symbols.createUninitializedInstance.owner - - private fun createSyntheticValuesPropertyDeclaration(enumEntries: List): IrProperty { - val startOffset = irClass.startOffset - val endOffset = irClass.endOffset - - val implObject = loweredEnum.implObject - val constructor = implObject.constructors.single() - - val irValuesInitializer = context.createArrayOfExpression( - startOffset, endOffset, - irClass.defaultType, - enumEntries - .sortedBy { it.name } - .map { - val initializer = it.initializerExpression?.expression - val entryConstructorCall = when { - initializer is IrConstructorCall -> initializer - - initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> - initializer.statements.last() as IrConstructorCall - - else -> error("Unexpected initializer: $initializer") - } - val entryClass = entryConstructorCall.symbol.owner.constructedClass - - irCall(startOffset, endOffset, - createUninitializedInstance, - listOf(entryClass.defaultType) - ) - - } - ) - val irField = loweredEnum.valuesField - context.createIrBuilder(constructor.symbol).run { - (constructor.body as IrBlockBody).statements += - irSetField(irGet(implObject.thisReceiver!!), irField, irValuesInitializer) - } - - val getter = loweredEnum.valuesGetter - context.createIrBuilder(getter.symbol).run { - getter.body = irBlockBody(irClass) { +irReturn(irGetField(irGetObject(implObject.symbol), irField)) } - } - - createValuesPropertyInitializer(enumEntries) - - return with(loweredEnum.valuesField.descriptor) { - IrPropertyImpl( - startOffset, endOffset, DECLARATION_ORIGIN_ENUM, IrPropertySymbolImpl(this), - name, visibility, modality, isVar, isConst, isLateInit, isDelegated, isExternal - ).apply { - this.backingField = irField - this.getter = getter - this.parent = implObject - } - } - } - - private val initInstanceSymbol = context.ir.symbols.initInstance - - private val arrayGetSymbol = context.ir.symbols.array.functions.single { it.owner.name == Name.identifier("get") } - - private val arrayType = context.ir.symbols.array.typeWith(irClass.defaultType) - - private fun createValuesPropertyInitializer(enumEntries: List) { - val startOffset = irClass.startOffset - val endOffset = irClass.endOffset - - fun IrBlockBuilder.initInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall = - irCall(initInstanceSymbol).apply { + private fun IrBlockBuilder.irInitInstanceCall(instance: IrCall, constructor: IrConstructorCall): IrCall = + irCall(initInstance).apply { putValueArgument(0, instance) putValueArgument(1, constructor) } - val implObject = loweredEnum.implObject - val constructor = implObject.constructors.single() - val irBuilder = context.createIrBuilder(constructor.symbol, startOffset, endOffset) - val valuesInitializer = irBuilder.irBlock(startOffset, endOffset) { + private fun buildValuesFieldInitializer(enumEntries: List) = irBuilder.run { + val irValuesInitializer = this@EnumClassLowering.context.createArrayOfExpression( + startOffset, endOffset, + irClass.defaultType, + enumEntries + .sortedBy { it.name } + .map { + val initializer = it.initializerExpression?.expression + val entryConstructorCall = when { + initializer is IrConstructorCall -> initializer + + initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> + initializer.statements.last() as IrConstructorCall + + else -> error("Unexpected initializer: $initializer") + } + val entryClass = entryConstructorCall.symbol.owner.constructedClass + + irCall(createUninitializedInstance, listOf(entryClass.defaultType)) + } + ) + irSetField(irGet(implObject.thisReceiver!!), valuesField, irValuesInitializer) + } + + private fun callEnumEntriesConstructors(enumEntries: List) = irBuilder.irBlock { val receiver = implObject.thisReceiver!! - val instances = irTemporary(irGetField(irGet(receiver), loweredEnum.valuesField)) - val enumIndices = enumEntries.sortedBy { it.name }.withIndex().associate { it.value to it.index } + val instances = irTemporary(irGetField(irGet(receiver), valuesField)) enumEntries.forEach { - val instance = irCall(arrayGetSymbol).apply { + val instance = irCall(arrayGet).apply { dispatchReceiver = irGet(instances) - putValueArgument(0, irInt(enumIndices[it]!!)) + putValueArgument(0, irInt(enumEntriesMap[it.name]!!.getterId)) } val initializer = it.initializerExpression!!.expression initializer.setDeclarationsParent(constructor) when { - initializer is IrConstructorCall -> +initInstanceCall(instance, initializer) + initializer is IrConstructorCall -> +irInitInstanceCall(instance, initializer) initializer is IrBlock && initializer.origin == ARGUMENTS_REORDERING_FOR_CALL -> { val statements = initializer.statements val constructorCall = statements.last() as IrConstructorCall - statements[statements.lastIndex] = initInstanceCall(instance, constructorCall) + statements[statements.lastIndex] = irInitInstanceCall(instance, constructorCall) +initializer } @@ -300,22 +247,11 @@ internal class EnumClassLowering(val context: Context) : FileLoweringPass { } // Needed for legacy MM targets that do not support threads. if (this@EnumClassLowering.context.memoryModel != MemoryModel.EXPERIMENTAL) { - +irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(arrayType)).apply { + +irCall(this@EnumClassLowering.context.ir.symbols.freeze, listOf(valuesField.type)).apply { extensionReceiver = irGet(receiver) } } } - (constructor.body as IrBlockBody).statements += valuesInitializer } - - private fun createSyntheticValuesMethodBody(declaration: IrFunction) = - context.createIrBuilder(declaration.symbol, irClass.startOffset, irClass.endOffset).irBlockBody { - +irReturn(with(enumSyntheticFunctionsBuilder) { enumValues(irClass) }) - } - - private fun createSyntheticValueOfMethodBody(declaration: IrFunction) = - context.createIrBuilder(declaration.symbol, irClass.startOffset, irClass.endOffset).irBlockBody { - +irReturn(with(enumSyntheticFunctionsBuilder) { enumValueOf(irClass, irGet(declaration.valueParameters[0])) }) - } } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumConstructorsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumConstructorsLowering.kt index e0b1d4125bf..b9518b8a003 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumConstructorsLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumConstructorsLowering.kt @@ -228,7 +228,7 @@ internal class EnumConstructorsLowering(val context: Context) : ClassLoweringPas override fun transform(enumConstructorCall: IrEnumConstructorCall): IrExpression { val name = enumEntry.name.asString() - val ordinal = context.enumsSupport.getEnumEntryOrdinal(enumEntry) + val ordinal = context.enumsSupport.enumEntriesMap(enumEntry.parentAsClass)[enumEntry.name]!!.ordinal val startOffset = enumConstructorCall.startOffset val endOffset = enumConstructorCall.endOffset diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumSpecialDeclarationsFactory.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumSpecialDeclarationsFactory.kt index 890a1f08d38..86093b64063 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumSpecialDeclarationsFactory.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumSpecialDeclarationsFactory.kt @@ -5,255 +5,118 @@ package org.jetbrains.kotlin.backend.konan.lower -import org.jetbrains.kotlin.backend.common.lower.createIrBuilder -import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.InternalAbi +import org.jetbrains.kotlin.backend.common.getOrPut +import org.jetbrains.kotlin.backend.konan.NativeMapping import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.DescriptorVisibilities -import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.IrBuiltIns +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter +import org.jetbrains.kotlin.ir.builders.declarations.buildClass +import org.jetbrains.kotlin.ir.builders.declarations.buildField import org.jetbrains.kotlin.ir.builders.declarations.buildFun -import org.jetbrains.kotlin.ir.builders.irBlockBody -import org.jetbrains.kotlin.ir.builders.irCall -import org.jetbrains.kotlin.ir.builders.irReturn import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.declarations.impl.IrClassImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrFieldImpl -import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.expressions.IrExpression -import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetFieldImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrGetObjectValueImpl -import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.impl.IrClassSymbolImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl -import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl -import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.typeWith import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.name.Name -internal class EnumsSupport(private val context: Context) { - private val enumSpecialDeclarationsFactory = EnumSpecialDeclarationsFactory(context) - private val internalLoweredEnums = context.mapping.internalLoweredEnums - private val externalLoweredEnums = context.mapping.externalLoweredEnums +internal class EnumsSupport( + mapping: NativeMapping, + symbols: KonanSymbols, + private val irBuiltIns: IrBuiltIns, + private val irFactory: IrFactory, +) { + private val enumImplObjects = mapping.enumImplObjects + private val enumValueGetters = mapping.enumValueGetters + private val enumEntriesMaps = mapping.enumEntriesMaps + private val array = symbols.array + private val genericValueOfSymbol = symbols.valueOfForEnum + private val genericValuesSymbol = symbols.valuesForEnum - fun getLoweredEnumOrNull(enumClass: IrClass): LoweredEnumAccess? { + fun enumEntriesMap(enumClass: IrClass): Map { require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } - return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) { - externalLoweredEnums[enumClass] - } else { - internalLoweredEnums[enumClass] + return enumEntriesMaps.getOrPut(enumClass) { + data class NameWithOrdinal(val name: Name, val ordinal: Int) + enumClass.declarations.asSequence() + .filterIsInstance() + .mapIndexed { index, it -> NameWithOrdinal(it.name, index) } + .sortedBy { it.name } + .withIndex() + .associate { it.value.name to LoweredEnumEntryDescription(it.value.ordinal, it.index) } + .toMap() } } - fun getLoweredEnum(enumClass: IrClass): LoweredEnumAccess { + fun getImplObject(enumClass: IrClass): IrClass { require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } - return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) { - externalLoweredEnums.getOrPut(enumClass) { - enumSpecialDeclarationsFactory.createExternalLoweredEnum(enumClass) - } - } else { - internalLoweredEnums.getOrPut(enumClass) { - enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass) + return enumImplObjects.getOrPut(enumClass) { + irFactory.buildClass { + startOffset = enumClass.startOffset + endOffset = enumClass.endOffset + origin = DECLARATION_ORIGIN_ENUM + name = "OBJECT".synthesizedName + kind = ClassKind.OBJECT + }.apply { + superTypes = listOf(irBuiltIns.anyType) + parent = enumClass + + addChild(irFactory.buildField { + startOffset = enumClass.startOffset + endOffset = enumClass.endOffset + origin = DECLARATION_ORIGIN_ENUM + name = "VALUES".synthesizedName + type = array.typeWith(enumClass.defaultType) + visibility = DescriptorVisibilities.PRIVATE + isFinal = true + }) } } } - fun getInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum { + fun getValueGetter(enumClass: IrClass): IrFunction { require(enumClass.isEnumClass) { "Expected enum class but was: ${enumClass.render()}" } - require(context.llvmModuleSpecification.containsDeclaration(enumClass)) { "Expected enum class from current module." } - return internalLoweredEnums.getOrPut(enumClass) { - enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass) + return enumValueGetters.getOrPut(enumClass) { + irFactory.buildFun { + startOffset = enumClass.startOffset + endOffset = enumClass.endOffset + origin = DECLARATION_ORIGIN_ENUM + name = "getEnumAt".synthesizedName + returnType = enumClass.defaultType + }.apply { + parent = enumClass + + addValueParameter { + name = Name.identifier("getterId") + origin = DECLARATION_ORIGIN_ENUM + type = irBuiltIns.intType + } + } } } - fun getEnumEntryOrdinal(enumEntry: IrEnumEntry) = - enumEntry.parentAsClass.declarations.filterIsInstance().indexOf(enumEntry) + fun getValuesField(implObject: IrClass) = implObject.fields.single() + + fun IrBuilderWithScope.irGetValuesField(enumClass: IrClass): IrExpression { + val implObject = getImplObject(enumClass) + val valuesField = getValuesField(implObject) + return irGetField(irGetObject(implObject.symbol), valuesField) + } + + fun IrBuilderWithScope.irEnumValues(enumClass: IrClass) = + irCall(genericValuesSymbol, listOf(enumClass.defaultType)).apply { + putValueArgument(0, irGetValuesField(enumClass)) + } + + fun IrBuilderWithScope.irEnumValueOf(enumClass: IrClass, value: IrExpression) = + irCall(genericValueOfSymbol, listOf(enumClass.defaultType)).apply { + putValueArgument(0, value) + putValueArgument(1, irGetValuesField(enumClass)) + } } internal object DECLARATION_ORIGIN_ENUM : IrDeclarationOriginImpl("ENUM") internal data class LoweredEnumEntryDescription(val ordinal: Int, val getterId: Int) - -/** - * Common interface for both [InternalLoweredEnum] and [ExternalLoweredEnum] - * that allows to work with lowered enum regardless of its location. - */ -internal interface LoweredEnumAccess { - val valuesGetter: IrSimpleFunction - val itemGetterSymbol: IrSimpleFunctionSymbol - val entriesMap: Map - fun getValuesField(startOffset: Int, endOffset: Int): IrExpression -} - -/** - * Represents lowered enum from current module. - */ -internal data class InternalLoweredEnum( - val implObject: IrClass, - val valuesField: IrField, - val valuesGetterWrapper: IrSimpleFunction, - override val valuesGetter: IrSimpleFunction, - override val itemGetterSymbol: IrSimpleFunctionSymbol, - override val entriesMap: Map -) : LoweredEnumAccess { - private fun internalObjectGetter(startOffset: Int, endOffset: Int) = - IrGetObjectValueImpl(startOffset, endOffset, - implObject.defaultType, - implObject.symbol - ) - - override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression = IrGetFieldImpl( - startOffset, - endOffset, - valuesField.symbol, - valuesField.type, - internalObjectGetter(startOffset, endOffset) - ) -} - -/** - * Represents lowered enum that's located in external module. - */ -internal data class ExternalLoweredEnum( - override val valuesGetter: IrSimpleFunction, - override val itemGetterSymbol: IrSimpleFunctionSymbol, - override val entriesMap: Map -) : LoweredEnumAccess { - override fun getValuesField(startOffset: Int, endOffset: Int): IrExpression = - IrCallImpl(startOffset, endOffset, valuesGetter.returnType, valuesGetter.symbol, valuesGetter.typeParameters.size, valuesGetter.valueParameters.size) -} - -internal class EnumSpecialDeclarationsFactory(val context: Context) { - private val symbols = context.ir.symbols - - private fun enumEntriesMap(enumClass: IrClass): Map { - data class NameWithOrdinal(val name: Name, val ordinal: Int) - return enumClass.declarations.asSequence() - .filterIsInstance() - .mapIndexed { index, it -> NameWithOrdinal(it.name, index) } - .sortedBy { it.name } - .withIndex() - .associate { it.value.name to LoweredEnumEntryDescription(it.value.ordinal, it.index) } - .toMap() - } - - private fun findItemGetterSymbol(): IrSimpleFunctionSymbol = - symbols.array.functions.single { it.descriptor.name == Name.identifier("get") } - - private fun valuesArrayType(enumClass: IrClass): IrType = - symbols.array.typeWith(enumClass.defaultType) - - // We can't move property getter to the top-level scope. - // So add a wrapper instead. - private fun createValuesGetterWrapper(enumClass: IrClass, isExternal: Boolean): IrSimpleFunction = - context.irFactory.buildFun { - name = InternalAbi.getEnumValuesAccessorName(enumClass) - returnType = valuesArrayType(enumClass) - origin = InternalAbi.INTERNAL_ABI_ORIGIN - this.isExternal = isExternal - }.also { - if (isExternal) { - context.internalAbi.reference(it, enumClass.module) - } else { - context.internalAbi.declare(it, enumClass.module) - } - } - - fun createExternalLoweredEnum(enumClass: IrClass): ExternalLoweredEnum { - val enumEntriesMap = enumEntriesMap(enumClass) - val itemGetterSymbol = findItemGetterSymbol() - val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = true) - return ExternalLoweredEnum(valuesGetterWrapper, itemGetterSymbol, enumEntriesMap) - } - - fun createInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum { - val startOffset = enumClass.startOffset - val endOffset = enumClass.endOffset - - val implObject = - IrClassImpl( - startOffset, endOffset, - DECLARATION_ORIGIN_ENUM, - IrClassSymbolImpl(), - "OBJECT".synthesizedName, - ClassKind.OBJECT, - DescriptorVisibilities.PUBLIC, - Modality.FINAL, - isCompanion = false, - isInner = false, - isData = false, - isExternal = false, - isValue = false, - isExpect = false, - isFun = false - ).apply { - parent = enumClass - createParameterDeclarations() - } - - val valuesType = valuesArrayType(enumClass) - val valuesField = - IrFieldImpl( - startOffset, endOffset, - DECLARATION_ORIGIN_ENUM, - IrFieldSymbolImpl(), - "VALUES".synthesizedName, - valuesType, - DescriptorVisibilities.PRIVATE, - isFinal = true, - isExternal = false, - isStatic = false, - ).apply { - parent = implObject - } - - val valuesGetter = - IrFunctionImpl( - startOffset, endOffset, - DECLARATION_ORIGIN_ENUM, - IrSimpleFunctionSymbolImpl(), - "get-VALUES".synthesizedName, - DescriptorVisibilities.PUBLIC, - Modality.FINAL, - valuesType, - isInline = false, - isExternal = false, - isTailrec = false, - isSuspend = false, - isExpect = false, - isFakeOverride = false, - isOperator = false, - isInfix = false - ).apply { - parent = implObject - } - - val constructorOfAny = context.irBuiltIns.anyClass.owner.constructors.first() - implObject.addSimpleDelegatingConstructor( - constructorOfAny, - context.irBuiltIns, - true // TODO: why primary? - ) - - implObject.superTypes += context.irBuiltIns.anyType - implObject.addFakeOverrides(context.typeSystem) - - val itemGetterSymbol = findItemGetterSymbol() - val enumEntriesMap = enumEntriesMap(enumClass) - val valuesGetterWrapper = createValuesGetterWrapper(enumClass, isExternal = false) - context.createIrBuilder(valuesGetterWrapper.symbol).run { - valuesGetterWrapper.body = irBlockBody { - +irReturn(irCall(valuesGetter)) - } - } - return InternalLoweredEnum( - implObject, - valuesField, - valuesGetterWrapper, - valuesGetter, - itemGetterSymbol, - enumEntriesMap) - } -}