[Linkage][Lowering] InternalABI
To effectively access enum entries during separate compilation (in other words, when compiling with caches), we need to reference Enum.$OBJECT.get-VALUES function. It is not a part of a public API because it is generated during EnumClassLowering. What are possible solutions? 1. Lazy IR. It is no-go, because there is no descriptor for it. 2. Generate IrFiles and appropriate packages during lowering. We can't do it, because lowerings are file-based and ConcurrentModificationException will occur. 2.1. Store declarations in list and add file later? Still not an option, because .parent (or module) for these declaration won't be initialized which breaks lowerings. 3. Something ugly. Here we go! We add a special file before lowerings which stores all declaration that form an internal ABI.
This commit is contained in:
committed by
Sergey Bogolepov
parent
9ea3ee9ad2
commit
31bad64728
+25
-6
@@ -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<IrClass, IrField>()
|
||||
private val loweredEnums = mutableMapOf<IrClass, LoweredEnum>()
|
||||
private val ordinals = mutableMapOf<ClassDescriptor, Map<ClassDescriptor, Int>>()
|
||||
private val internalLoweredEnums = mutableMapOf<IrClass, InternalLoweredEnum>()
|
||||
private val externalLoweredEnums = mutableMapOf<IrClass, ExternalLoweredEnum>()
|
||||
|
||||
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<String, LLVMTypeRef> = HashMap()
|
||||
|
||||
/**
|
||||
* Manages internal ABI references and declarations.
|
||||
*/
|
||||
val internalAbi = InternalAbi(this)
|
||||
}
|
||||
|
||||
private fun MemberScope.getContributedClassifier(name: String) =
|
||||
|
||||
+127
-17
@@ -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<Name, 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<Name, Int>
|
||||
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<Name, Int>
|
||||
) : 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<Name, Int>
|
||||
) : 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<Name, Int> =
|
||||
enumClass.declarations
|
||||
.filterIsInstance<IrEnumEntry>()
|
||||
.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<IrEnumEntry>()
|
||||
.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)
|
||||
|
||||
+78
@@ -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<ModuleDescriptor, IrFile>()
|
||||
|
||||
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") {}
|
||||
}
|
||||
}
|
||||
+2
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -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() {
|
||||
|
||||
+5
-22
@@ -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() {
|
||||
|
||||
Reference in New Issue
Block a user