diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 04f68af0734..3ba5dc135ef 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -55,8 +55,8 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal internal class SpecialDeclarationsFactory(val context: Context) { private val enumSpecialDeclarationsFactory by lazy { EnumSpecialDeclarationsFactory(context) } private val outerThisFields = mutableMapOf() - private val loweredEnums = mutableMapOf() - private val ordinals = mutableMapOf>() + private val internalLoweredEnums = mutableMapOf() + private val externalLoweredEnums = mutableMapOf() private data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections) @@ -103,10 +103,24 @@ internal class SpecialDeclarationsFactory(val context: Context) { } } - fun getLoweredEnum(enumClass: IrClass): LoweredEnum { + fun getLoweredEnum(enumClass: IrClass): LoweredEnumAccess { assert(enumClass.kind == ClassKind.ENUM_CLASS) { "Expected enum class but was: ${enumClass.descriptor}" } - return loweredEnums.getOrPut(enumClass) { - enumSpecialDeclarationsFactory.createLoweredEnum(enumClass) + return if (!context.llvmModuleSpecification.containsDeclaration(enumClass)) { + externalLoweredEnums.getOrPut(enumClass) { + enumSpecialDeclarationsFactory.createExternalLoweredEnum(enumClass) + } + } else { + internalLoweredEnums.getOrPut(enumClass) { + enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass) + } + } + } + + fun getInternalLoweredEnum(enumClass: IrClass): InternalLoweredEnum { + assert(enumClass.kind == ClassKind.ENUM_CLASS) { "Expected enum class but was: ${enumClass.descriptor}" } + assert(context.llvmModuleSpecification.containsDeclaration(enumClass)) { "Expected enum class from current module." } + return internalLoweredEnums.getOrPut(enumClass) { + enumSpecialDeclarationsFactory.createInternalLoweredEnum(enumClass) } } @@ -278,7 +292,7 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { throw Error("Another IrModule in the context.") } field = module!! - + internalAbi.init(module) ir = KonanIr(this, module) } @@ -419,6 +433,11 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { ) val declaredLocalArrays: MutableMap = HashMap() + + /** + * Manages internal ABI references and declarations. + */ + val internalAbi = InternalAbi(this) } private fun MemberScope.getContributedClassifier(name: String) = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt index 12615faeb08..44d1d9c4bf4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EnumSpecialDescriptorsFactory.kt @@ -8,10 +8,15 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.common.ir.addFakeOverridesViaIncorrectHeuristic import org.jetbrains.kotlin.backend.common.ir.addSimpleDelegatingConstructor import org.jetbrains.kotlin.backend.common.ir.createParameterDeclarations +import org.jetbrains.kotlin.backend.common.lower.createIrBuilder import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName import org.jetbrains.kotlin.descriptors.ClassKind -import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET +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 @@ -19,27 +24,131 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrFunctionImpl import org.jetbrains.kotlin.ir.descriptors.WrappedClassDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedFieldDescriptor import org.jetbrains.kotlin.ir.descriptors.WrappedSimpleFunctionDescriptor +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 +import org.jetbrains.kotlin.resolve.descriptorUtil.module internal object DECLARATION_ORIGIN_ENUM : IrDeclarationOriginImpl("ENUM") -internal data class LoweredEnum(val implObject: IrClass, - val valuesField: IrField, - val valuesGetter: IrSimpleFunction, - val itemGetterSymbol: IrSimpleFunctionSymbol, - val entriesMap: Map) +/** + * 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) +} internal class EnumSpecialDeclarationsFactory(val context: Context) { private val symbols = context.ir.symbols - fun createLoweredEnum(enumClass: IrClass): LoweredEnum { + private fun enumEntriesMap(enumClass: IrClass): Map = + enumClass.declarations + .filterIsInstance() + .sortedBy { it.name } + .withIndex() + .associate { it.value.name to 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 { + return WrappedSimpleFunctionDescriptor().let { + val valuesType = valuesArrayType(enumClass) + val origin = if (isExternal) InternalAbi.INTERNAL_ABI_ORIGIN else DECLARATION_ORIGIN_ENUM + val name = context.internalAbi.getMangledNameFor("get-VALUES", enumClass) + IrFunctionImpl( + UNDEFINED_OFFSET, UNDEFINED_OFFSET, + origin, + IrSimpleFunctionSymbolImpl(it), + name, + DescriptorVisibilities.PUBLIC, + Modality.FINAL, + valuesType, + isInline = false, + isExternal = isExternal, + isTailrec = false, + isSuspend = false, + isExpect = false, + isFakeOverride = false, + isOperator = false, + isInfix = false + ).apply { + it.bind(this) + if (isExternal) { + context.internalAbi.reference(this, enumClass.module) + } else { + context.internalAbi.declare(this) + } + } + } + } + + 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 @@ -66,7 +175,7 @@ internal class EnumSpecialDeclarationsFactory(val context: Context) { } } - val valuesType = symbols.array.typeWith(enumClass.defaultType) + val valuesType = valuesArrayType(enumClass) val valuesField = WrappedFieldDescriptor().let { IrFieldImpl( startOffset, endOffset, @@ -117,17 +226,18 @@ internal class EnumSpecialDeclarationsFactory(val context: Context) { implObject.superTypes += context.irBuiltIns.anyType implObject.addFakeOverridesViaIncorrectHeuristic() - val itemGetterSymbol = symbols.array.functions.single { it.descriptor.name == Name.identifier("get") } - val enumEntriesMap = enumClass.declarations - .filterIsInstance() - .sortedBy { it.name } - .withIndex() - .associate { it.value.name to it.index } - .toMap() - - return LoweredEnum( + 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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt new file mode 100644 index 00000000000..81b05c3653d --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InternalAbi.kt @@ -0,0 +1,78 @@ +/* + * Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the LICENSE file. + */ +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.backend.common.ir.addChild +import org.jetbrains.kotlin.backend.konan.descriptors.synthesizedName +import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl +import org.jetbrains.kotlin.ir.util.* +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.name.Name + +/** + * Sometimes we need to reference symbols that are not declared in metadata. + * For example, symbol might be declared during lowering. + * In case of compiler caches, this means that it is not accessible as Lazy IR + * and we have to explicitly add an external declaration. + */ +internal class InternalAbi(private val context: Context) { + /** + * File that stores all internal ABI declarations. + * + * We have to store such declarations in top-level to avoid mangling that + * makes referencing harder. + * A bit better solution is to add files with proper packages, but it is impossible + * during FileLowering (hello, ConcurrentModificationException). + */ + private lateinit var internalAbiFile: IrFile + + /** + * Representation of ABI files from external modules. + */ + private val externalModules = mutableMapOf() + + fun init(module: IrModuleFragment) { + internalAbiFile = createAbiFile(module) + } + + private fun createAbiFile(module: IrModuleFragment): IrFile = + module.addFile(NaiveSourceBasedFileEntryImpl("internal"), FqName("kotlin.native.caches.abi")) + + /** + * Adds external [function] from [module] to a list of external references. + */ + fun reference(function: IrFunction, module: ModuleDescriptor) { + assert(function.isExternal) { "Function that represents external ABI should be marked as external" } + context.llvmImports.add(module.llvmSymbolOrigin) + externalModules.getOrPut(module) { + createAbiFile(IrModuleFragmentImpl(module, context.irBuiltIns)) + }.addChild(function) + } + + /** + * Generate name for declaration that will be a part of internal ABI. + */ + fun getMangledNameFor(declarationName: String, parent: IrDeclarationParent): Name { + val prefix = parent.fqNameForIrSerialization + return "$prefix.$declarationName".synthesizedName + } + + /** + * Adds [function] to a list of publicly available symbols. + */ + fun declare(function: IrFunction) { + internalAbiFile.addChild(function) + } + + companion object { + /** + * Allows to distinguish external declarations to internal ABI. + */ + val INTERNAL_ABI_ORIGIN = object : IrDeclarationOriginImpl("INTERNAL_ABI") {} + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 8e86cd3e360..3b5f13a5cb7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -297,6 +297,8 @@ fun IrFunction.externalSymbolOrThrow(): String? { if (annotations.hasAnnotation(RuntimeNames.cCall)) return null + if (origin == InternalAbi.INTERNAL_ABI_ORIGIN) return null + throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName or @ObjCMethod annotation") } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 74d8d884641..6888ee44744 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -173,7 +173,8 @@ internal interface ContextUtils : RuntimeAware { * It may be declared as external function prototype. */ val IrFunction.llvmFunction: LLVMValueRef - get() = llvmFunctionOrNull ?: error("$name in $file/${parent.fqNameForIrSerialization}") + get() = llvmFunctionOrNull + ?: error("$name in $file/${parent.fqNameForIrSerialization}") val IrFunction.llvmFunctionOrNull: LLVMValueRef? get() { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt index b2032bda7c0..c88cff06495 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/EnumClassLowering.kt @@ -31,7 +31,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name -internal class EnumSyntheticFunctionsBuilder(val context: Context) { +private class EnumSyntheticFunctionsBuilder(val context: Context) { fun buildValuesExpression(startOffset: Int, endOffset: Int, enumClass: IrClass): IrExpression { @@ -39,16 +39,7 @@ internal class EnumSyntheticFunctionsBuilder(val context: Context) { return irCall(startOffset, endOffset, genericValuesSymbol.owner, listOf(enumClass.defaultType)) .apply { - val receiver = IrGetObjectValueImpl(startOffset, endOffset, - loweredEnum.implObject.defaultType, - loweredEnum.implObject.symbol) - putValueArgument(0, IrGetFieldImpl( - startOffset, - endOffset, - loweredEnum.valuesField.symbol, - loweredEnum.valuesField.type, - receiver - )) + putValueArgument(0, loweredEnum.getValuesField(startOffset, endOffset)) } } @@ -60,15 +51,7 @@ internal class EnumSyntheticFunctionsBuilder(val context: Context) { return irCall(startOffset, endOffset, genericValueOfSymbol.owner, listOf(enumClass.defaultType)) .apply { putValueArgument(0, value) - val receiver = IrGetObjectValueImpl(startOffset, endOffset, - loweredEnum.implObject.defaultType, loweredEnum.implObject.symbol) - putValueArgument(1, IrGetFieldImpl( - startOffset, - endOffset, - loweredEnum.valuesField.symbol, - loweredEnum.valuesField.type, - receiver - )) + putValueArgument(1, loweredEnum.getValuesField(startOffset, endOffset)) } } @@ -126,7 +109,7 @@ internal class EnumUsageLowering(val context: Context) private fun loadEnumEntry(startOffset: Int, endOffset: Int, enumClass: IrClass, name: Name): IrExpression { val loweredEnum = context.specialDeclarationsFactory.getLoweredEnum(enumClass) - val ordinal = loweredEnum.entriesMap[name]!! + val ordinal = loweredEnum.entriesMap.getValue(name) return IrCallImpl( startOffset, endOffset, enumClass.defaultType, loweredEnum.itemGetterSymbol.owner.symbol, @@ -154,7 +137,7 @@ internal class EnumClassLowering(val context: Context) : ClassLoweringPass { } private inner class EnumClassTransformer(val irClass: IrClass) { - private val loweredEnum = context.specialDeclarationsFactory.getLoweredEnum(irClass) + private val loweredEnum = context.specialDeclarationsFactory.getInternalLoweredEnum(irClass) private val enumSyntheticFunctionsBuilder = EnumSyntheticFunctionsBuilder(context) fun run() {