[K/N][IR][codegen] Use LazyIr for cached libraries

It's not that simple because we still need inline functions bodies
and classes fields which aren't present in Lazy IR. To overcome this,
save additional binary info for a cached library and then use it when needed
This commit is contained in:
Igor Chevdar
2021-08-24 21:14:52 +05:00
parent f865238062
commit 0cece79856
31 changed files with 1042 additions and 140 deletions
@@ -182,7 +182,7 @@ class FunctionInlining(
val statements = (copiedCallee.body as IrBlockBody).statements
val irReturnableBlockSymbol = IrReturnableBlockSymbolImpl()
val endOffset = callee.endOffset
val endOffset = statements.lastOrNull()?.endOffset ?: callee.endOffset
/* creates irBuilder appending to the end of the given returnable block: thus why we initialize
* irBuilder with (..., endOffset, endOffset).
*/
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.DeserializableClass
import org.jetbrains.kotlin.ir.util.TypeTranslator
import org.jetbrains.kotlin.ir.util.isObject
import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
@@ -79,7 +80,8 @@ class IrLazyClass(
private fun shouldBuildStub(descriptor: DeclarationDescriptor): Boolean =
descriptor !is DeclarationDescriptorWithVisibility ||
!DescriptorVisibilities.isPrivate(descriptor.visibility)
!DescriptorVisibilities.isPrivate(descriptor.visibility) ||
isObject && descriptor is ClassConstructorDescriptor
override var typeParameters: List<IrTypeParameter> by lazyVar(stubGenerator.lock) {
descriptor.declaredTypeParameters.mapTo(arrayListOf()) {
@@ -7,11 +7,15 @@ package org.jetbrains.kotlin.ir.declarations.lazy
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.DescriptorVisibility
import org.jetbrains.kotlin.descriptors.ParameterDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
import org.jetbrains.kotlin.ir.expressions.IrExpressionBody
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.symbols.IrValueParameterSymbol
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
import org.jetbrains.kotlin.ir.util.TypeTranslator
@@ -20,6 +24,35 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DescriptorWithContainerSource
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedContainerSource
@OptIn(ObsoleteDescriptorBasedAPI::class)
class IrLazyValueParameter(
override val startOffset: Int,
override val endOffset: Int,
override var origin: IrDeclarationOrigin,
override val symbol: IrValueParameterSymbol,
override val descriptor: ValueParameterDescriptor,
override val name: Name,
override val index: Int,
override var type: IrType,
override var varargElementType: IrType?,
override val isCrossinline: Boolean,
override val isNoinline: Boolean,
override val isHidden: Boolean,
override val isAssignable: Boolean,
override val stubGenerator: DeclarationStubGenerator,
override val typeTranslator: TypeTranslator,
) : IrValueParameter(), IrLazyDeclarationBase {
override lateinit var parent: IrDeclarationParent
override var defaultValue: IrExpressionBody? = null
override var annotations: List<IrConstructorCall> by createLazyAnnotations()
init {
symbol.bind(this)
}
}
@OptIn(ObsoleteDescriptorBasedAPI::class)
class IrLazyConstructor(
override val startOffset: Int,
@@ -5,10 +5,7 @@
package org.jetbrains.kotlin.ir.declarations.lazy
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.IrLock
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.util.DeclarationStubGenerator
@@ -32,6 +29,16 @@ class IrLazySymbolTable(private val originalTable: SymbolTable) : ReferenceSymbo
}
}
override fun referenceTypeAlias(descriptor: TypeAliasDescriptor): IrTypeAliasSymbol {
synchronized(lock) {
return originalTable.referenceTypeAlias(descriptor).also {
if (!it.isBound) {
stubGenerator?.generateTypeAliasStub(descriptor)
}
}
}
}
override fun referenceConstructor(descriptor: ClassConstructorDescriptor): IrConstructorSymbol {
synchronized(lock) {
return originalTable.referenceConstructor(descriptor).also {
@@ -62,6 +69,16 @@ class IrLazySymbolTable(private val originalTable: SymbolTable) : ReferenceSymbo
}
}
override fun referenceProperty(descriptor: PropertyDescriptor): IrPropertySymbol {
synchronized(lock) {
return originalTable.referenceProperty(descriptor).also {
if (!it.isBound) {
stubGenerator?.generatePropertyStub(descriptor)
}
}
}
}
override fun referenceTypeParameter(classifier: TypeParameterDescriptor): IrTypeParameterSymbol {
synchronized(lock) {
return originalTable.referenceTypeParameter(classifier).also {
@@ -22,7 +22,7 @@ import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.symbols.IrFileSymbol
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.symbols.IrReturnableBlockSymbol
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.transformInPlace
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -58,6 +58,5 @@ abstract class IrReturnableBlock : IrBlock(), IrSymbolOwner, IrReturnTarget {
abstract val inlineFunctionSymbol: IrFunctionSymbol?
}
@Suppress("unused") // Used in kotlin-native
val IrReturnableBlock.sourceFileSymbol: IrFileSymbol?
get() = inlineFunctionSymbol?.owner?.file?.symbol
get() = inlineFunctionSymbol?.owner?.fileOrNull?.symbol
@@ -66,7 +66,7 @@ fun buildFakeOverrideMember(superType: IrType, member: IrOverridableMember, claz
val typeParameters = extractTypeParameters(classifier.owner)
val superArguments = superType.arguments
assert(typeParameters.size == superArguments.size) {
require(typeParameters.size == superArguments.size) {
"typeParameters = $typeParameters size != typeArguments = $superArguments size "
}
@@ -76,7 +76,7 @@ fun buildFakeOverrideMember(superType: IrType, member: IrOverridableMember, claz
val tp = typeParameters[i]
val ta = superArguments[i]
require(ta is IrTypeProjection) { "Unexpected super type argument: ${ta.render()} @ $i" }
assert(ta.variance == Variance.INVARIANT) { "Unexpected variance in super type argument: ${ta.variance} @$i" }
require(ta.variance == Variance.INVARIANT) { "Unexpected variance in super type argument: ${ta.variance} @$i" }
substitutionMap[tp.symbol] = ta.type
}
@@ -414,7 +414,7 @@ class IrOverridingUtil(
fakeOverride.overriddenSymbols = effectiveOverridden.map { it.original.symbol }
assert(
require(
fakeOverride.overriddenSymbols.isNotEmpty()
) { "Overridden symbols should be set for " + CallableMemberDescriptor.Kind.FAKE_OVERRIDE }
@@ -465,26 +465,24 @@ class IrOverridingUtil(
val bReturnType = b.returnType
if (!isVisibilityMoreSpecific(a, b)) return false
if (a is IrSimpleFunction) {
assert(b is IrSimpleFunction) { "b is " + b.javaClass }
require(b is IrSimpleFunction) { "b is " + b.javaClass }
return isReturnTypeMoreSpecific(a, aReturnType, b, bReturnType)
}
if (a is IrProperty) {
assert(b is IrProperty) { "b is " + b.javaClass }
val pa = a
val pb = b as IrProperty
require(b is IrProperty) { "b is " + b.javaClass }
if (!isAccessorMoreSpecific(
pa.setter,
pb.setter
a.setter,
b.setter
)
) return false
return if (pa.isVar && pb.isVar) {
return if (a.isVar && b.isVar) {
createTypeCheckerState(
a.getter!!.typeParameters,
b.getter!!.typeParameters
).equalTypes(aReturnType, bReturnType)
} else {
// both vals or var vs val: val can't be more specific then var
!(!pa.isVar && pb.isVar) && isReturnTypeMoreSpecific(
!(!a.isVar && b.isVar) && isReturnTypeMoreSpecific(
a, aReturnType,
b, bReturnType
)
@@ -510,7 +508,7 @@ class IrOverridingUtil(
private fun selectMostSpecificMember(
overridables: Collection<IrOverridableMember>
): IrOverridableMember {
assert(!overridables.isEmpty()) { "Should have at least one overridable descriptor" }
require(!overridables.isEmpty()) { "Should have at least one overridable descriptor" }
if (overridables.size == 1) {
return overridables.first()
}
@@ -693,7 +691,7 @@ class IrOverridingUtil(
}
*/
assert(superValueParameters.size == subValueParameters.size)
require(superValueParameters.size == subValueParameters.size)
superValueParameters.forEachIndexed { index, parameter ->
if (!AbstractTypeChecker.equalTypes(
@@ -235,10 +235,9 @@ abstract class DeclarationStubGenerator(
private fun KotlinType.toIrType() = typeTranslator.translateType(this)
internal fun generateValueParameterStub(descriptor: ValueParameterDescriptor): IrValueParameter = with(descriptor) {
symbolTable.irFactory.createValueParameter(
UNDEFINED_OFFSET, UNDEFINED_OFFSET, computeOrigin(this), IrValueParameterSymbolImpl(this), name, index, type.toIrType(),
varargElementType?.toIrType(), isCrossinline, isNoinline, isHidden = false, isAssignable = false
).also { irValueParameter ->
IrLazyValueParameter(UNDEFINED_OFFSET, UNDEFINED_OFFSET, computeOrigin(this), IrValueParameterSymbolImpl(this), this, name, index,
type.toIrType(), varargElementType?.toIrType(), isCrossinline, isNoinline, isHidden = false, isAssignable = false, this@DeclarationStubGenerator, typeTranslator)
.also { irValueParameter ->
if (descriptor.declaresDefaultValue()) {
irValueParameter.defaultValue = irValueParameter.createStubDefaultValue()
}
@@ -304,7 +303,7 @@ abstract class DeclarationStubGenerator(
}
internal fun generateOrGetScopedTypeParameterStub(descriptor: TypeParameterDescriptor): IrTypeParameter {
val referenced = symbolTable.referenceTypeParameter(descriptor)
val referenced = symbolTable.referenceScopedTypeParameter(descriptor)
if (referenced.isBound) {
return referenced.owner
}
@@ -40,6 +40,7 @@ interface ReferenceSymbolTable {
fun referenceValueParameter(descriptor: ParameterDescriptor): IrValueParameterSymbol
fun referenceTypeParameter(classifier: TypeParameterDescriptor): IrTypeParameterSymbol
fun referenceScopedTypeParameter(classifier: TypeParameterDescriptor): IrTypeParameterSymbol
fun referenceVariable(descriptor: VariableDescriptor): IrVariableSymbol
fun referenceTypeAlias(descriptor: TypeAliasDescriptor): IrTypeAliasSymbol
@@ -1030,6 +1031,11 @@ open class SymbolTable(
createTypeParameterSymbol(classifier)
}
override fun referenceScopedTypeParameter(classifier: TypeParameterDescriptor): IrTypeParameterSymbol =
scopedTypeParameterSymbolTable.referenced(classifier) {
createTypeParameterSymbol(classifier)
}
override fun referenceGlobalTypeParameterFromLinker(sig: IdSignature): IrTypeParameterSymbol {
return globalTypeParameterSymbolTable.referenced(sig, false) {
if (sig.isPubliclyVisible) IrTypeParameterPublicSymbolImpl(sig) else IrTypeParameterSymbolImpl()
@@ -35,7 +35,7 @@ abstract class BasicIrModuleDeserializer(
private val moduleDeserializationState = ModuleDeserializationState(linker, this)
internal val moduleReversedFileIndex = mutableMapOf<IdSignature, FileDeserializationState>()
val moduleReversedFileIndex = mutableMapOf<IdSignature, FileDeserializationState>()
override val moduleDependencies by lazy {
moduleDescriptor.allDependencyModules.filter { it != moduleDescriptor }.map { linker.resolveModuleDeserializer(it, null) }
@@ -110,6 +110,7 @@ abstract class BasicIrModuleDeserializer(
val fileDeserializationState = FileDeserializationState(
linker,
fileIndex,
file,
fileReader,
fileProto,
@@ -57,8 +57,9 @@ class IrFileDeserializer(
class FileDeserializationState(
val linker: KotlinIrLinker,
val fileIndex: Int,
file: IrFile,
fileReader: IrLibraryFileFromBytes,
val fileReader: IrLibraryFileFromBytes,
fileProto: ProtoFile,
deserializeBodies: Boolean,
allowErrorNodes: Boolean,
@@ -22,6 +22,7 @@ internal fun IrSymbol.kind(): BinarySymbolData.SymbolKind {
is IrPropertySymbol -> BinarySymbolData.SymbolKind.PROPERTY_SYMBOL
is IrEnumEntrySymbol -> BinarySymbolData.SymbolKind.ENUM_ENTRY_SYMBOL
is IrTypeAliasSymbol -> BinarySymbolData.SymbolKind.TYPEALIAS_SYMBOL
is IrTypeParameterSymbol -> BinarySymbolData.SymbolKind.TYPE_PARAMETER_SYMBOL
else -> error("Unexpected symbol kind $this")
}
}
@@ -208,6 +208,7 @@ CLASS IR_EXTERNAL_DECLARATION_STUB CLASS name:Int modality:FINAL visibility:publ
VALUE_PARAMETER IR_EXTERNAL_DECLARATION_STUB name:other index:0 type:kotlin.Int
CLASS IR_EXTERNAL_DECLARATION_STUB OBJECT name:Companion modality:FINAL visibility:public [companion] superTypes:[<unbound IrClassPublicSymbolImpl>]
$this: VALUE_PARAMETER IR_EXTERNAL_DECLARATION_STUB name:<this> type:kotlin.Int.Companion
CONSTRUCTOR IR_EXTERNAL_DECLARATION_STUB visibility:private <> () returnType:kotlin.Int.Companion [primary]
PROPERTY IR_EXTERNAL_DECLARATION_STUB name:MAX_VALUE visibility:public modality:FINAL [const,val]
FIELD IR_EXTERNAL_DECLARATION_STUB name:MAX_VALUE type:kotlin.Int visibility:public [final]
EXPRESSION_BODY
@@ -364,6 +364,15 @@ class K2Native : CLICompiler<K2NativeCompilerArguments>() {
WorkerExceptionHandling.LEGACY
}
})
put(LAZY_IR_FOR_CACHES, when (arguments.lazyIrForCaches) {
null -> true
"enable" -> true
"disable" -> false
else -> {
configuration.report(ERROR, "Expected 'enable' or 'disable' for lazy IR usage for cached libraries")
false
}
})
arguments.externalDependencies?.let { put(EXTERNAL_DEPENDENCIES, it) }
putIfNotNull(LLVM_VARIANT, when (val variant = arguments.llvmVariant) {
@@ -361,6 +361,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() {
@Argument(value = "-Xruntime-logs", valueDescription = "<tag1=level1,tag2=level2,...>", description = "Enable logging for runtime with tags.")
var runtimeLogs: String? = null
@Argument(value = "-Xlazy-ir-for-caches", valueDescription = "{disable|enable}", description = "Use lazy IR for cached libraries")
var lazyIrForCaches: String? = null
override fun configureAnalysisFlags(collector: MessageCollector, languageVersion: LanguageVersion): MutableMap<AnalysisFlag<*>, Any> =
super.configureAnalysisFlags(collector, languageVersion).also {
val optInList = it[AnalysisFlags.optIn] as List<*>
@@ -53,7 +53,7 @@ internal val Context.getBoxFunction: (IrClass) -> IrSimpleFunction by Context.la
classes.add(parent)
parent = parent.parent
}
require(parent is IrFile) { "Local inline classes are not supported" }
require(parent is IrFile || parent is IrExternalPackageFragment) { "Local inline classes are not supported" }
val symbols = ir.symbols
@@ -100,7 +100,7 @@ internal val Context.getBoxFunction: (IrClass) -> IrSimpleFunction by Context.la
).also {
it.parent = function
})
function.parent = inlinedClass.getContainingFile()!!
function.parent = parent
}
}
@@ -112,7 +112,7 @@ internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context.
classes.add(parent)
parent = parent.parent
}
require(parent is IrFile) { "Local inline classes are not supported" }
require(parent is IrFile || parent is IrExternalPackageFragment) { "Local inline classes are not supported" }
val symbols = ir.symbols
@@ -159,7 +159,7 @@ internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context.
).also {
it.parent = function
})
function.parent = inlinedClass.getContainingFile()!!
function.parent = parent
}
}
@@ -36,11 +36,65 @@ import org.jetbrains.kotlin.util.OperatorNameConventions
internal object DECLARATION_ORIGIN_FUNCTION_CLASS : IrDeclarationOriginImpl("DECLARATION_ORIGIN_FUNCTION_CLASS")
internal class BuiltInFictitiousFunctionIrClassFactory(
var symbolTable: SymbolTable?,
abstract class KonanIrAbstractDescriptorBasedFunctionFactory : IrProvider, IrAbstractDescriptorBasedFunctionFactory()
internal class LazyIrFunctionFactory(
private val symbolTable: SymbolTable,
private val stubGenerator: DeclarationStubGenerator,
private val irBuiltIns: IrBuiltInsOverDescriptors,
private val reflectionTypes: KonanReflectionTypes
) : IrProvider, IrAbstractDescriptorBasedFunctionFactory() {
) : KonanIrAbstractDescriptorBasedFunctionFactory() {
override fun getDeclaration(symbol: IrSymbol) =
(symbol.descriptor as? FunctionClassDescriptor)?.let { descriptor ->
buildClass(descriptor) {
declareClass(descriptor) {
createIrClass(descriptor)
}
}
}
override fun functionClassDescriptor(arity: Int): FunctionClassDescriptor =
irBuiltIns.builtIns.getFunction(arity) as FunctionClassDescriptor
override fun kFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
reflectionTypes.getKFunction(arity) as FunctionClassDescriptor
override fun suspendFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
irBuiltIns.builtIns.getSuspendFunction(arity) as FunctionClassDescriptor
override fun kSuspendFunctionClassDescriptor(arity: Int): FunctionClassDescriptor =
reflectionTypes.getKSuspendFunction(arity) as FunctionClassDescriptor
override fun functionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(irBuiltIns.builtIns.getFunction(arity) as FunctionClassDescriptor, declarator)
override fun kFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(reflectionTypes.getKFunction(arity) as FunctionClassDescriptor, declarator)
override fun suspendFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(irBuiltIns.builtIns.getSuspendFunction(arity) as FunctionClassDescriptor, declarator)
override fun kSuspendFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(reflectionTypes.getKSuspendFunction(arity) as FunctionClassDescriptor, declarator)
private val builtClassesMap = mutableMapOf<FunctionClassDescriptor, IrClass>()
private fun createIrClass(descriptor: ClassDescriptor): IrClass =
stubGenerator.generateClassStub(descriptor)
private fun createClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
symbolTable.declarator { createIrClass(descriptor) }
private fun buildClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
builtClassesMap.getOrPut(descriptor) { createClass(descriptor, declarator) }
}
internal class BuiltInFictitiousFunctionIrClassFactory(
private val symbolTable: SymbolTable,
private val irBuiltIns: IrBuiltInsOverDescriptors,
private val reflectionTypes: KonanReflectionTypes
) : KonanIrAbstractDescriptorBasedFunctionFactory() {
override fun getDeclaration(symbol: IrSymbol) =
(symbol.descriptor as? FunctionClassDescriptor)?.let { descriptor ->
@@ -98,11 +152,11 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
override fun kSuspendFunctionN(arity: Int, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
buildClass(reflectionTypes.getKSuspendFunction(arity) as FunctionClassDescriptor, declarator)
private val functionSymbol = symbolTable!!.referenceClass(
private val functionSymbol = symbolTable.referenceClass(
irBuiltIns.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.function))!!)
private val kFunctionSymbol = symbolTable!!.referenceClass(
private val kFunctionSymbol = symbolTable.referenceClass(
irBuiltIns.builtIns.builtInsModule.findClassAcrossModuleDependencies(
ClassId.topLevel(KonanFqNames.kFunction))!!)
@@ -121,20 +175,10 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
}
private fun createTypeParameter(descriptor: TypeParameterDescriptor): IrTypeParameter =
symbolTable?.declareGlobalTypeParameter(
symbolTable.declareGlobalTypeParameter(
SYNTHETIC_OFFSET, SYNTHETIC_OFFSET, DECLARATION_ORIGIN_FUNCTION_CLASS,
descriptor
)
?: IrTypeParameterImpl(
SYNTHETIC_OFFSET,
SYNTHETIC_OFFSET,
DECLARATION_ORIGIN_FUNCTION_CLASS,
IrTypeParameterSymbolImpl(descriptor),
descriptor.name,
descriptor.index,
descriptor.isReified,
descriptor.variance
)
private fun createSimpleFunction(
descriptor: FunctionDescriptor,
@@ -150,16 +194,14 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
)
}
}
return symbolTable?.declareSimpleFunction(descriptor, functionFactory)
?: functionFactory(IrSimpleFunctionSymbolImpl(descriptor))
return symbolTable.declareSimpleFunction(descriptor, functionFactory)
}
private fun createIrClass(symbol: IrClassSymbol, descriptor: ClassDescriptor): IrClass =
IrFactoryImpl.createIrClassFromDescriptor(offset, offset, DECLARATION_ORIGIN_FUNCTION_CLASS, symbol, descriptor)
private fun createClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
symbolTable?.declarator { createIrClass(it, descriptor) }
?: createIrClass(IrClassSymbolImpl(descriptor), descriptor)
symbolTable.declarator { createIrClass(it, descriptor) }
private fun buildClass(descriptor: FunctionClassDescriptor, declarator: SymbolTable.((IrClassSymbol) -> IrClass) -> IrClass): IrClass =
builtClassesMap.getOrPut(descriptor) {
@@ -246,8 +288,7 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
private fun toIrType(wrapped: KotlinType): IrType {
val kotlinType = wrapped.unwrap()
return with(IrSimpleTypeBuilder()) {
classifier =
symbolTable?.referenceClassifier(kotlinType.constructor.declarationDescriptor ?: error("No classifier for type $kotlinType"))
classifier = symbolTable.referenceClassifier(kotlinType.constructor.declarationDescriptor ?: error("No classifier for type $kotlinType"))
hasQuestionMark = kotlinType.isMarkedNullable
arguments = kotlinType.arguments.map {
if (it.isStarProjection) IrStarProjectionImpl
@@ -296,11 +337,10 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
}
}
val newFunction = symbolTable?.declareSimpleFunction(descriptor, functionDeclare)
?: functionDeclare(IrSimpleFunctionSymbolImpl(descriptor))
val newFunction = symbolTable.declareSimpleFunction(descriptor, functionDeclare)
newFunction.parent = this
newFunction.overriddenSymbols = descriptor.overriddenDescriptors.mapNotNull { symbolTable?.referenceSimpleFunction(it.original) }
newFunction.overriddenSymbols = descriptor.overriddenDescriptors.mapNotNull { symbolTable.referenceSimpleFunction(it.original) }
newFunction.dispatchReceiverParameter = descriptor.dispatchReceiverParameter?.let { newFunction.createValueParameter(it) }
newFunction.extensionReceiverParameter = descriptor.extensionReceiverParameter?.let { newFunction.createValueParameter(it) }
newFunction.valueParameters = descriptor.valueParameters.map { newFunction.createValueParameter(it) }
@@ -327,8 +367,7 @@ internal class BuiltInFictitiousFunctionIrClassFactory(
isExpect = descriptor.isExpect,
isFakeOverride = true)
}
val property = symbolTable?.declareProperty(offset, offset, memberOrigin, descriptor, propertyFactory = propertyDeclare)
?: propertyDeclare(IrPropertySymbolImpl(descriptor))
val property = symbolTable.declareProperty(offset, offset, memberOrigin, descriptor, propertyFactory = propertyDeclare)
property.parent = this
property.getter = descriptor.getter?.let { g -> createFakeOverrideFunction(g, property.symbol) }
@@ -5,6 +5,8 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget
@@ -25,6 +27,18 @@ class CachedLibraries(
val directory = File(path).absoluteFile.parent
File(directory, BITCODE_DEPENDENCIES_FILE_NAME).readStrings()
}
val serializedInlineFunctionBodies by lazy {
val directory = File(path).absoluteFile.parent
val data = File(directory, INLINE_FUNCTION_BODIES_FILE_NAME).readBytes()
InlineFunctionBodyReferenceSerializer.deserialize(data)
}
val serializedClassFields by lazy {
val directory = File(path).absoluteFile.parent
val data = File(directory, CLASS_FIELDS_FILE_NAME).readBytes()
ClassFieldsSerializer.deserialize(data)
}
}
private val cacheDirsContents = mutableMapOf<String, Set<String>>()
@@ -90,6 +104,9 @@ class CachedLibraries(
companion object {
fun getCachedLibraryName(library: KotlinLibrary): String = getCachedLibraryName(library.uniqueName)
fun getCachedLibraryName(libraryName: String): String = "$libraryName-cache"
const val BITCODE_DEPENDENCIES_FILE_NAME = "bitcode_deps"
const val INLINE_FUNCTION_BODIES_FILE_NAME = "inline_bodies"
const val CLASS_FIELDS_FILE_NAME = "class_fields"
}
}
@@ -42,6 +42,9 @@ import org.jetbrains.kotlin.backend.common.ir.copyTo
import org.jetbrains.kotlin.backend.common.ir.copyToWithoutSuperTypes
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker
import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields
import org.jetbrains.kotlin.backend.konan.serialization.SerializedInlineFunctionReference
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
@@ -51,6 +54,8 @@ import org.jetbrains.kotlin.konan.util.disposeNativeMemoryAllocator
import org.jetbrains.kotlin.library.SerializedIrModule
import org.jetbrains.kotlin.resolve.descriptorUtil.isEffectivelyExternal
internal class InlineFunctionOriginInfo(val irFile: IrFile, val startOffset: Int, val endOffset: Int)
/**
* Offset for synthetic elements created by lowerings and not attributable to other places in the source code.
*/
@@ -65,7 +70,7 @@ internal class SpecialDeclarationsFactory(val context: Context) {
private val bridges = mutableMapOf<BridgeKey, IrSimpleFunction>()
val loweredInlineFunctions = mutableSetOf<IrFunction>()
val loweredInlineFunctions = mutableMapOf<IrFunction, InlineFunctionOriginInfo>()
object DECLARATION_ORIGIN_FIELD_FOR_OUTER_THIS :
IrDeclarationOriginImpl("FIELD_FOR_OUTER_THIS")
@@ -494,6 +499,12 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) {
* Manages internal ABI references and declarations.
*/
val internalAbi = InternalAbi(this)
lateinit var irLinker: KonanIrLinker
val inlineFunctionBodies = mutableListOf<SerializedInlineFunctionReference>()
val classFields = mutableListOf<SerializedClassFields>()
}
private fun MemberScope.getContributedClassifier(name: String) =
@@ -19,14 +19,24 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrTryImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrSimpleFunctionSymbolImpl
import org.jetbrains.kotlin.ir.symbols.impl.IrValueParameterSymbolImpl
import org.jetbrains.kotlin.ir.types.typeWith
import org.jetbrains.kotlin.ir.util.SYNTHETIC_OFFSET
import org.jetbrains.kotlin.ir.util.irCatch
import org.jetbrains.kotlin.name.Name
internal fun makeEntryPoint(context: Context): IrFunction {
val actualMain = context.ir.symbols.entryPoint!!.owner
// TODO: Do we need to do something with the offsets if <main> is in a cached library?
val startOffset = if (context.llvmModuleSpecification.containsDeclaration(actualMain))
actualMain.startOffset
else
SYNTHETIC_OFFSET
val endOffset = if (context.llvmModuleSpecification.containsDeclaration(actualMain))
actualMain.endOffset
else
SYNTHETIC_OFFSET
val entryPoint = IrFunctionImpl(
actualMain.startOffset,
actualMain.startOffset,
startOffset,
endOffset,
IrDeclarationOrigin.DEFINED,
IrSimpleFunctionSymbolImpl(),
Name.identifier("Konan_start"),
@@ -44,7 +54,7 @@ internal fun makeEntryPoint(context: Context): IrFunction {
).also { function ->
function.valueParameters = listOf(
IrValueParameterImpl(
actualMain.startOffset, actualMain.startOffset,
startOffset, endOffset,
IrDeclarationOrigin.DEFINED,
IrValueParameterSymbolImpl(),
Name.identifier("args"),
@@ -60,16 +70,12 @@ internal fun makeEntryPoint(context: Context): IrFunction {
})
}
entryPoint.annotations += buildSimpleAnnotation(context.irBuiltIns,
actualMain.startOffset, actualMain.startOffset,
startOffset, endOffset,
context.ir.symbols.exportForCppRuntime.owner, "Konan_start")
val builder = context.createIrBuilder(entryPoint.symbol)
entryPoint.body = builder.irBlockBody(entryPoint) {
+IrTryImpl(
startOffset = actualMain.startOffset,
endOffset = actualMain.startOffset,
type = context.irBuiltIns.nothingType
).apply {
+IrTryImpl(startOffset, endOffset, context.irBuiltIns.nothingType).apply {
tryResult = irBlock {
+irCall(actualMain).apply {
if (actualMain.valueParameters.size != 0)
@@ -273,6 +273,8 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration
internal val isInteropStubs: Boolean get() = manifestProperties?.getProperty("interop") == "true"
internal val propertyLazyInitialization: Boolean get() = configuration.get(KonanConfigKeys.PROPERTY_LAZY_INITIALIZATION)!!
internal val lazyIrForCaches: Boolean get() = configuration.get(KonanConfigKeys.LAZY_IR_FOR_CACHES)!!
}
fun CompilerConfiguration.report(priority: CompilerMessageSeverity, message: String)
@@ -170,6 +170,7 @@ class KonanConfigKeys {
CompilerConfigurationKey.create("use external dependencies to enhance IR linker error messages")
val LLVM_VARIANT: CompilerConfigurationKey<LlvmVariant?> = CompilerConfigurationKey.create("llvm variant")
val RUNTIME_LOGS: CompilerConfigurationKey<String> = CompilerConfigurationKey.create("enable runtime logging")
val LAZY_IR_FOR_CACHES: CompilerConfigurationKey<Boolean> = CompilerConfigurationKey.create("use lazy IR for cached libraries")
}
}
@@ -1,5 +1,7 @@
package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
import org.jetbrains.kotlin.konan.KonanExternalToolFailure
import org.jetbrains.kotlin.konan.exec.Command
import org.jetbrains.kotlin.konan.file.File
@@ -58,6 +60,8 @@ internal class Linker(val context: Context) {
private fun saveAdditionalInfoForCache() {
saveCacheBitcodeDependencies()
saveInlineFunctionBodies()
saveClassFields()
}
private fun saveCacheBitcodeDependencies() {
@@ -73,6 +77,18 @@ internal class Linker(val context: Context) {
bitcodeDependenciesFile.writeLines(bitcodeDependencies.map { it.uniqueName })
}
private fun saveInlineFunctionBodies() {
val outputFiles = context.config.outputFiles
val inlineFunctionBodiesFile = File(outputFiles.inlineFunctionBodiesFile!!)
inlineFunctionBodiesFile.writeBytes(InlineFunctionBodyReferenceSerializer.serialize(context.inlineFunctionBodies))
}
private fun saveClassFields() {
val outputFiles = context.config.outputFiles
val classFieldsFile = File(outputFiles.classFieldsFile!!)
classFieldsFile.writeBytes(ClassFieldsSerializer.serialize(context.classFields))
}
private fun renameOutput() {
if (context.config.produce.isCache) {
val outputFiles = context.config.outputFiles
@@ -63,6 +63,16 @@ class OutputFiles(outputPath: String?, target: KonanTarget, val produce: Compile
tempCacheDirectory!!.child(CachedLibraries.BITCODE_DEPENDENCIES_FILE_NAME).absolutePath
else null
val inlineFunctionBodiesFile =
if (produce.isCache)
tempCacheDirectory!!.child(CachedLibraries.INLINE_FUNCTION_BODIES_FILE_NAME).absolutePath
else null
val classFieldsFile =
if (produce.isCache)
tempCacheDirectory!!.child(CachedLibraries.CLASS_FIELDS_FILE_NAME).absolutePath
else null
private fun String.fullOutputName() = prefixBaseNameIfNeeded(prefix).suffixIfNeeded(suffix)
private fun String.prefixBaseNameIfNeeded(prefix: String) =
@@ -53,15 +53,17 @@ internal fun Context.psiToIr(
// Note: using [llvmModuleSpecification] since this phase produces IR for generating single LLVM module.
val exportedDependencies = (getExportedDependencies() + modulesWithoutDCE).distinct()
val irBuiltInsOverDescriptors = generatorContext.irBuiltIns as IrBuiltInsOverDescriptors
val functionIrClassFactory = BuiltInFictitiousFunctionIrClassFactory(
symbolTable, irBuiltInsOverDescriptors, reflectionTypes)
irBuiltInsOverDescriptors.functionFactory = functionIrClassFactory
val stubGenerator = DeclarationStubGeneratorImpl(
moduleDescriptor, symbolTable,
generatorContext.irBuiltIns
)
val irBuiltInsOverDescriptors = generatorContext.irBuiltIns as IrBuiltInsOverDescriptors
val functionIrClassFactory: KonanIrAbstractDescriptorBasedFunctionFactory =
if (config.lazyIrForCaches && !llvmModuleSpecification.containsModule(this.stdlibModule))
LazyIrFunctionFactory(symbolTable, stubGenerator, irBuiltInsOverDescriptors, reflectionTypes)
else
BuiltInFictitiousFunctionIrClassFactory(symbolTable, irBuiltInsOverDescriptors, reflectionTypes)
irBuiltInsOverDescriptors.functionFactory = functionIrClassFactory
val symbols = KonanSymbols(this, generatorContext.irBuiltIns, symbolTable, symbolTable.lazyWrapper)
val irDeserializer = if (isProducingLibrary && !useLinkerWhenProducingLibrary) {
@@ -77,6 +79,7 @@ internal fun Context.psiToIr(
}
}
} else {
val exportedDependencies = (getExportedDependencies() + modulesWithoutDCE).distinct()
val irProviderForCEnumsAndCStructs =
IrProviderForCEnumAndCStructStubs(generatorContext, interopBuiltIns, symbols)
@@ -102,6 +105,7 @@ internal fun Context.psiToIr(
irProviderForCEnumsAndCStructs,
exportedDependencies,
config.cachedLibraries,
config.lazyIrForCaches,
config.userVisibleIrModulesSupport
).also { linker ->
@@ -120,15 +124,14 @@ internal fun Context.psiToIr(
}
for (dependency in sortDependencies(dependencies).filter { it != moduleDescriptor }) {
val kotlinLibrary = dependency.getCapability(KlibModuleOrigin.CAPABILITY)?.let {
(it as? DeserializedKlibModuleOrigin)?.library
}
when {
isProducingLibrary -> linker.deserializeOnlyHeaderModule(dependency, kotlinLibrary)
kotlinLibrary != null && config.cachedLibraries.isLibraryCached(kotlinLibrary) ->
linker.deserializeHeadersWithInlineBodies(dependency, kotlinLibrary)
else -> linker.deserializeIrModuleHeader(dependency, kotlinLibrary, dependency.name.asString())
}
val kotlinLibrary = (dependency.getCapability(KlibModuleOrigin.CAPABILITY) as? DeserializedKlibModuleOrigin)?.library
val isCachedLibrary = kotlinLibrary != null && config.cachedLibraries.isLibraryCached(kotlinLibrary)
if (isProducingLibrary || (config.lazyIrForCaches && isCachedLibrary))
linker.deserializeOnlyHeaderModule(dependency, kotlinLibrary)
else if (isCachedLibrary)
linker.deserializeHeadersWithInlineBodies(dependency, kotlinLibrary!!)
else
linker.deserializeIrModuleHeader(dependency, kotlinLibrary, dependency.name.asString())
}
if (dependencies.size == dependenciesCount) break
dependenciesCount = dependencies.size
@@ -140,7 +143,6 @@ internal fun Context.psiToIr(
.filter(ModuleDescriptor::isFromInteropLibrary)
.forEach(irProviderForCEnumsAndCStructs::referenceAllEnumsAndStructsFrom)
translator.addPostprocessingStep {
irProviderForCEnumsAndCStructs.generateBodies()
}
@@ -197,14 +199,19 @@ internal fun Context.psiToIr(
// Note: coupled with [shouldLower] below.
irModules = modules.filterValues { llvmModuleSpecification.containsModule(it) }
if (!isProducingLibrary)
irLinker = irDeserializer as KonanIrLinker
ir.symbols = symbols
if (!isProducingLibrary) {
// TODO: find out what should be done in the new builtins/symbols about it
if (this.stdlibModule in modulesWithoutDCE)
functionIrClassFactory.buildAllClasses()
if (this.stdlibModule in modulesWithoutDCE) {
(functionIrClassFactory as BuiltInFictitiousFunctionIrClassFactory).buildAllClasses()
}
internalAbi.init(irModules.values + irModule!!)
functionIrClassFactory.module = (modules.values + irModule!!).single { it.descriptor.isNativeStdlib() }
(functionIrClassFactory as? BuiltInFictitiousFunctionIrClassFactory)?.module =
(modules.values + irModule!!).single { it.descriptor.isNativeStdlib() }
}
mainModule.files.forEach { it.metadata = KonanFileMetadataSource(mainModule) }
@@ -7,13 +7,12 @@ import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.common.phaser.*
import org.jetbrains.kotlin.backend.common.serialization.CompatibilityMode
import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataMonolithicSerializer
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier
import org.jetbrains.kotlin.backend.konan.lower.SamSuperTypesChecker
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.serialization.KonanIdSignaturer
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrModuleSerializer
import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerDesc
import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.config.CommonConfigurationKeys
import org.jetbrains.kotlin.config.languageVersionSettings
import org.jetbrains.kotlin.ir.IrElement
@@ -32,6 +31,7 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.name.FqName
internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) {
if (!context.config.needVerifyIr) return
@@ -124,6 +124,47 @@ internal val psiToIrPhase = konanUnitPhase(
prerequisite = setOf(createSymbolTablePhase)
)
internal val buildAdditionalCacheInfoPhase = konanUnitPhase(
op = {
irModules.values.single().let { module ->
val moduleDeserializer = irLinker.nonCachedLibraryModuleDeserializers[module.descriptor]
if (moduleDeserializer == null) {
require(module.descriptor.isFromInteropLibrary()) { "No module deserializer for ${module.descriptor}" }
} else {
val compatibleMode = CompatibilityMode(moduleDeserializer.libraryAbiVersion).oldSignatures
module.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this)
}
override fun visitClass(declaration: IrClass) {
declaration.acceptChildrenVoid(this)
if (declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS)
classFields.add(moduleDeserializer.buildClassFields(declaration, getLayoutBuilder(declaration).getDeclaredFields()))
}
override fun visitFunction(declaration: IrFunction) {
declaration.acceptChildrenVoid(this)
if (declaration.isFakeOverride || !declaration.isExportedInlineFunction) return
inlineFunctionBodies.add(moduleDeserializer.buildInlineFunctionReference(declaration))
}
private val IrClass.isExported
get() = with(KonanManglerIr) { isExported(compatibleMode) }
private val IrFunction.isExportedInlineFunction
get() = isInline && with(KonanManglerIr) { isExported(compatibleMode) }
})
}
}
},
name = "BuildAdditionalCacheInfo",
description = "Build additional cache info (inline functions bodies and fields of classes)",
prerequisite = setOf(psiToIrPhase)
)
// Coupled with [psiToIrPhase] logic above.
internal fun shouldLower(context: Context, declaration: IrDeclaration): Boolean {
return context.llvmModuleSpecification.containsDeclaration(declaration)
@@ -305,21 +346,17 @@ internal val entryPointPhase = makeCustomPhase<Context, IrModuleFragment>(
description = "Add entry point for program",
prerequisite = emptySet(),
op = { context, _ ->
assert(context.config.produce == CompilerOutputKind.PROGRAM)
require(context.config.produce == CompilerOutputKind.PROGRAM)
val originalFile = context.ir.symbols.entryPoint!!.owner.file
val originalModule = originalFile.packageFragmentDescriptor.containingDeclaration
val file = if (context.llvmModuleSpecification.containsModule(originalModule)) {
originalFile
val entryPoint = context.ir.symbols.entryPoint!!.owner
val file = if (context.llvmModuleSpecification.containsDeclaration(entryPoint)) {
entryPoint.file
} else {
// `main` function is compiled to other LLVM module.
// For example, test running support uses `main` defined in stdlib.
context.irModule!!.addFile(originalFile.fileEntry, originalFile.fqName)
context.irModule!!.addFile(NaiveSourceBasedFileEntryImpl("entryPointOwner"), FqName("kotlin.native.caches.abi"))
}
require(context.llvmModuleSpecification.containsModule(
file.packageFragmentDescriptor.containingDeclaration))
file.addChild(makeEntryPoint(context))
}
)
@@ -444,6 +481,7 @@ val toplevelPhase: CompilerPhase<*, Unit, Unit> = namedUnitPhase(
objCExportPhase then
buildCExportsPhase then
psiToIrPhase then
buildAdditionalCacheInfoPhase then
destroySymbolTablePhase then
copyDefaultValuesToActualPhase then
checkSamSuperTypesPhase then
@@ -478,6 +516,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) {
// Don't serialize anything to a final executable.
disableUnless(serializerPhase, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM)
disableUnless(buildAdditionalCacheInfoPhase, config.produce.isCache)
disableUnless(exportInternalAbiPhase, config.produce.isCache)
disableIf(backendCodegen, config.produce == CompilerOutputKind.LIBRARY)
disableUnless(bitcodeOptimizationPhase, config.produce.involvesLinkStage)
@@ -24,6 +24,7 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.library.isInterop
internal class OverriddenFunctionInfo(
val function: IrSimpleFunction,
@@ -466,6 +467,19 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context, va
* Fields declared in the class.
*/
fun getDeclaredFields(): List<FieldInfo> {
if (context.config.lazyIrForCaches && !context.llvmModuleSpecification.containsDeclaration(irClass)) {
val packageFragment = irClass.findPackage()
val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration
if (moduleDescriptor.isFromInteropLibrary())
return emptyList()
val moduleDeserializer = context.irLinker.cachedLibraryModuleDeserializers[moduleDescriptor]
?: error("No module deserializer for ${irClass.render()}")
val fields = moduleDeserializer.deserializeClassFields(irClass).toMutableList()
if (irClass.isInner)
fields.add(InnerClassLowering.addOuterThisField(mutableListOf(), irClass, context).toFieldInfo())
return fields
}
val declarations: List<IrDeclaration> = if (irClass.isInner && !isLowered) {
// Note: copying to avoid mutation of the original class.
irClass.declarations.toMutableList()
@@ -6,20 +6,16 @@
package org.jetbrains.kotlin.backend.konan.ir
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.llvm.llvmSymbolOrigin
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyDeclarationBase
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.ir.util.file
// This file contains some IR utilities which actually use descriptors.
// TODO: port this code to IR.
internal val IrDeclaration.llvmSymbolOrigin get() = when (this) {
is IrLazyDeclarationBase -> descriptor.llvmSymbolOrigin
else -> file.packageFragmentDescriptor.llvmSymbolOrigin
}
internal val IrDeclaration.llvmSymbolOrigin get() = findPackage().packageFragmentDescriptor.llvmSymbolOrigin
internal fun IrType.isObjCObjectType() = this.toKotlinType().isObjCObjectType()
@@ -1824,15 +1824,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
//-------------------------------------------------------------------------//
private inner class ReturnableBlockScope(val returnableBlock: IrReturnableBlock) :
FileScope(returnableBlock.sourceFileSymbol?.owner ?: (currentCodeContext.fileScope() as? FileScope)?.file ?: error("returnable block should belong to current file at least") ) {
FileScope(returnableBlock.inlineFunctionSymbol?.owner?.let {
context.specialDeclarationsFactory.loweredInlineFunctions[it]?.irFile ?: it.fileOrNull
}
?: (currentCodeContext.fileScope() as? FileScope)?.file
?: error("returnable block should belong to current file at least")) {
var bbExit : LLVMBasicBlockRef? = null
var resultPhi : LLVMValueRef? = null
private val functionScope by lazy { returnableBlock.inlineFunctionSymbol?.owner?.scope() }
private val functionScope by lazy {
returnableBlock.inlineFunctionSymbol?.owner?.let {
it.scope(file().fileEntry.line(context.specialDeclarationsFactory.loweredInlineFunctions[it]?.startOffset ?: it.startOffset))
}
}
private fun getExit(): LLVMBasicBlockRef {
val location = returnableBlock.inlineFunctionSymbol?.let {
location(it.owner.endOffset)
val location = returnableBlock.inlineFunctionSymbol?.owner?.let {
location(context.specialDeclarationsFactory.loweredInlineFunctions[it]?.endOffset ?: it.endOffset)
} ?: returnableBlock.statements.lastOrNull()?.let {
location(it.endOffset)
}
@@ -12,25 +12,38 @@ import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFr
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering
import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLambdasLowering
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.InlineFunctionOriginInfo
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.file
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.util.render
// TODO: This is a bit hacky. Think about adopting persistent IR ideas.
internal class NativeInlineFunctionResolver(override val context: Context) : DefaultInlineFunctionResolver(context) {
override fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction {
val function = super.getFunctionDeclaration(symbol)
val body = function.body ?: return function
if (function in context.specialDeclarationsFactory.loweredInlineFunctions)
return function
context.specialDeclarationsFactory.loweredInlineFunctions.add(function)
val packageFragment = function.findPackage()
if (packageFragment !is IrExternalPackageFragment) {
context.specialDeclarationsFactory.loweredInlineFunctions[function] =
InlineFunctionOriginInfo(function.file, function.startOffset, function.endOffset)
} else {
// The function is from Lazy IR, get its body from the IR linker.
val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration
val moduleDeserializer = context.irLinker.cachedLibraryModuleDeserializers[moduleDescriptor]
?: error("No module deserializer for ${function.render()}")
context.specialDeclarationsFactory.loweredInlineFunctions[function] = moduleDeserializer.deserializeInlineFunction(function)
}
PreInlineLowering(context).lower(body, function)
val body = function.body ?: return function
PreInlineLowering(context).lower(body, function, context.specialDeclarationsFactory.loweredInlineFunctions[function]!!.irFile)
ArrayConstructorLowering(context).lower(body, function)
@@ -44,7 +57,7 @@ internal class NativeInlineFunctionResolver(override val context: Context) : Def
LocalClassesInInlineLambdasLowering(context).lower(body, function)
if (context.llvmModuleSpecification.containsPackageFragment(function.getPackageFragment()!!)) {
if (context.llvmModuleSpecification.containsPackageFragment(packageFragment)) {
// Do not extract local classes off of inline functions from cached libraries.
LocalClassesInInlineFunctionsLowering(context).lower(body, function)
LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, function)
@@ -14,6 +14,7 @@ import org.jetbrains.kotlin.backend.konan.KonanConfigKeys
import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrDeclarationBase
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrSymbolOwner
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
@@ -35,7 +36,9 @@ internal class PreInlineLowering(val context: Context) : BodyLoweringPass {
private val asserts = symbols.asserts
private val enableAssertions = context.config.configuration.getBoolean(KonanConfigKeys.ENABLE_ASSERTIONS)
override fun lower(irBody: IrBody, container: IrDeclaration) {
override fun lower(irBody: IrBody, container: IrDeclaration) = lower(irBody, container, container.file)
fun lower(irBody: IrBody, container: IrDeclaration, irFile: IrFile) {
irBody.transformChildren(object : IrElementTransformer<IrBuilderWithScope> {
override fun visitDeclaration(declaration: IrDeclarationBase, data: IrBuilderWithScope) =
super.visitDeclaration(declaration,
@@ -53,7 +56,7 @@ internal class PreInlineLowering(val context: Context) : BodyLoweringPass {
IrCompositeImpl(expression.startOffset, expression.endOffset, expression.type)
}
Symbols.isTypeOfIntrinsic(expression.symbol) -> {
with (KTypeGenerator(context, container.file, expression, needExactTypeParameters = true)) {
with (KTypeGenerator(context, irFile, expression, needExactTypeParameters = true)) {
data.at(expression).irKType(expression.getTypeArgument(0)!!, leaveReifiedForLater = true)
}
}
@@ -16,12 +16,20 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.lower.parents
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideBuilder
import org.jetbrains.kotlin.backend.common.overrides.FakeOverrideClassFilter
import org.jetbrains.kotlin.backend.common.overrides.FileLocalAwareLinker
import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinaryNameAndType
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.konan.CachedLibraries
import org.jetbrains.kotlin.backend.common.serialization.encodings.FunctionFlags
import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.InlineFunctionOriginInfo
import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.descriptors.*
@@ -31,11 +39,17 @@ import org.jetbrains.kotlin.descriptors.konan.klibModuleOrigin
import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl
import org.jetbrains.kotlin.ir.symbols.IrSymbol
import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.symbols.impl.IrPublicSymbolBase
import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl
import org.jetbrains.kotlin.ir.types.classOrNull
import org.jetbrains.kotlin.ir.types.classifierOrNull
import org.jetbrains.kotlin.ir.types.defaultType
import org.jetbrains.kotlin.ir.types.getPublicSignature
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
@@ -44,7 +58,237 @@ import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.protobuf.ExtensionRegistryLite
import org.jetbrains.kotlin.resolve.descriptorUtil.module
import org.jetbrains.kotlin.backend.common.serialization.proto.IrFile as ProtoFile
import org.jetbrains.kotlin.backend.common.serialization.proto.IrDeclaration as ProtoDeclaration
import org.jetbrains.kotlin.backend.common.serialization.proto.IrClass as ProtoClass
import org.jetbrains.kotlin.backend.common.serialization.proto.IrFunction as ProtoFunction
import org.jetbrains.kotlin.backend.common.serialization.proto.IrProperty as ProtoProperty
import org.jetbrains.kotlin.backend.common.serialization.proto.IrField as ProtoField
import sun.misc.Unsafe
private val unsafe = with(Unsafe::class.java.getDeclaredField("theUnsafe")) {
isAccessible = true
return@with this.get(null) as Unsafe
}
private val byteArrayBaseOffset = unsafe.arrayBaseOffset(ByteArray::class.java).toLong()
internal class ByteArrayStream(val buf: ByteArray) {
private var offset = 0
fun hasData() = offset < buf.size
fun readInt(): Int {
checkSize(offset + Int.SIZE_BYTES) { "Can't read an int at $offset, size = ${buf.size}" }
return unsafe.getInt(buf, byteArrayBaseOffset + offset).also { offset += Int.SIZE_BYTES }
}
fun writeInt(value: Int) {
checkSize(offset + Int.SIZE_BYTES) { "Can't write an int at $offset, size = ${buf.size}" }
unsafe.putInt(buf, byteArrayBaseOffset + offset, value).also { offset += Int.SIZE_BYTES }
}
private fun checkSize(at: Int, messageBuilder: () -> String) {
if (at > buf.size) error(messageBuilder())
}
}
class SerializedInlineFunctionReference(val file: Int, val functionSignature: Int, val body: Int,
val startOffset: Int, val endOffset: Int,
val extensionReceiverSig: Int, val dispatchReceiverSig: Int,
val valueParameterSigs: IntArray, val typeParameterSigs: IntArray,
val defaultValues: IntArray)
internal object InlineFunctionBodyReferenceSerializer {
fun serialize(bodies: List<SerializedInlineFunctionReference>): ByteArray {
val size = bodies.sumOf {
Int.SIZE_BYTES * (10 + it.valueParameterSigs.size + it.typeParameterSigs.size + it.defaultValues.size)
}
val stream = ByteArrayStream(ByteArray(size))
bodies.forEach {
stream.writeInt(it.file)
stream.writeInt(it.functionSignature)
stream.writeInt(it.body)
stream.writeInt(it.startOffset)
stream.writeInt(it.endOffset)
stream.writeInt(it.extensionReceiverSig)
stream.writeInt(it.dispatchReceiverSig)
stream.writeInt(it.valueParameterSigs.size)
it.valueParameterSigs.forEach { sig -> stream.writeInt(sig) }
stream.writeInt(it.typeParameterSigs.size)
it.typeParameterSigs.forEach { sig -> stream.writeInt(sig) }
stream.writeInt(it.defaultValues.size)
it.defaultValues.forEach { stream.writeInt(it) }
}
return stream.buf
}
fun deserialize(data: ByteArray): List<SerializedInlineFunctionReference> {
val result = mutableListOf<SerializedInlineFunctionReference>()
val stream = ByteArrayStream(data)
while (stream.hasData()) {
val file = stream.readInt()
val functionSignature = stream.readInt()
val body = stream.readInt()
val startOffset = stream.readInt()
val endOffset = stream.readInt()
val extensionReceiverSig = stream.readInt()
val dispatchReceiverSig = stream.readInt()
val valueParameterSigsCount = stream.readInt()
val valueParameterSigs = IntArray(valueParameterSigsCount) { stream.readInt() }
val typeParameterSigsCount = stream.readInt()
val typeParameterSigs = IntArray(typeParameterSigsCount) { stream.readInt() }
val defaultValuesCount = stream.readInt()
val defaultValues = IntArray(defaultValuesCount) { stream.readInt() }
result.add(SerializedInlineFunctionReference(file, functionSignature, body, startOffset, endOffset,
extensionReceiverSig, dispatchReceiverSig, valueParameterSigs, typeParameterSigs, defaultValues))
}
return result
}
}
// [binaryType] is needed in case a field is of a private inline class type (which can't be deserialized).
// But it is safe to just set the field's type to the primitive type the inline class will be erased to.
class SerializedClassFieldInfo(val name: Int, val binaryType: Int, val type: Int, val flags: Int) {
companion object {
const val FLAG_IS_CONST = 1
const val FLAG_CONST_INITIALIZER = 2
}
}
class SerializedClassFields(val file: Int, val classSignature: Int,
val typeParameterSigs: IntArray, val fields: Array<SerializedClassFieldInfo>)
internal object ClassFieldsSerializer {
fun serialize(classFields: List<SerializedClassFields>): ByteArray {
val size = classFields.sumOf { Int.SIZE_BYTES * (4 + it.typeParameterSigs.size + it.fields.size * 4) }
val stream = ByteArrayStream(ByteArray(size))
classFields.forEach {
stream.writeInt(it.file)
stream.writeInt(it.classSignature)
stream.writeInt(it.typeParameterSigs.size)
it.typeParameterSigs.forEach { stream.writeInt(it) }
stream.writeInt(it.fields.size)
it.fields.forEach { field ->
stream.writeInt(field.name)
stream.writeInt(field.binaryType)
stream.writeInt(field.type)
stream.writeInt(field.flags)
}
}
return stream.buf
}
fun deserialize(data: ByteArray): List<SerializedClassFields> {
val result = mutableListOf<SerializedClassFields>()
val stream = ByteArrayStream(data)
while (stream.hasData()) {
val file = stream.readInt()
val classSignature = stream.readInt()
val typeParameterSigsCount = stream.readInt()
val typeParameterSigs = IntArray(typeParameterSigsCount) { stream.readInt() }
val fieldsCount = stream.readInt()
val fields = Array(fieldsCount) {
val name = stream.readInt()
val binaryType = stream.readInt()
val type = stream.readInt()
val flags = stream.readInt()
SerializedClassFieldInfo(name, binaryType, type, flags)
}
result.add(SerializedClassFields(file, classSignature, typeParameterSigs, fields))
}
return result
}
}
internal fun ProtoClass.findClass(irClass: IrClass, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoClass {
val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}")
var result: ProtoClass? = null
for (i in 0 until this.declarationCount) {
val child = this.getDeclaration(i)
if (child.declaratorCase != ProtoDeclaration.DeclaratorCase.IR_CLASS) continue
val childClass = child.irClass
val name = fileReader.string(child.irClass.name)
if (name == irClass.name.asString()) {
if (result == null)
result = childClass
else {
val resultIdSignature = symbolDeserializer.deserializeIdSignature(BinarySymbolData.decode(result.base.symbol).signatureId)
if (resultIdSignature == signature)
return result
result = childClass
}
}
}
return result ?: error("Class ${irClass.render()} is not found")
}
internal fun ProtoClass.findProperty(irProperty: IrProperty, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoProperty {
val signature = irProperty.symbol.signature ?: error("No signature for ${irProperty.render()}")
var result: ProtoProperty? = null
for (i in 0 until this.declarationCount) {
val child = this.getDeclaration(i)
if (child.declaratorCase != ProtoDeclaration.DeclaratorCase.IR_PROPERTY) continue
val childProperty = child.irProperty
val name = fileReader.string(child.irProperty.name)
if (name == irProperty.name.asString()) {
if (result == null)
result = childProperty
else {
val resultIdSignature = symbolDeserializer.deserializeIdSignature(BinarySymbolData.decode(result.base.symbol).signatureId)
if (resultIdSignature == signature)
return result
result = childProperty
}
}
}
return result ?: error("Property ${irProperty.render()} is not found")
}
internal fun ProtoProperty.findAccessor(irProperty: IrProperty, irFunction: IrSimpleFunction): ProtoFunction {
if (irFunction == irProperty.getter)
return getter
require(irFunction == irProperty.setter) { "Accessor should be either a getter or a setter. ${irFunction.render()}" }
return setter
}
internal fun ProtoClass.findInlineFunction(irFunction: IrFunction, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoFunction {
(irFunction as? IrSimpleFunction)?.correspondingPropertySymbol?.owner?.let { irProperty ->
return findProperty(irProperty, fileReader, symbolDeserializer).findAccessor(irProperty, irFunction)
}
val signature = irFunction.symbol.signature ?: error("No signature for ${irFunction.render()}")
var result: ProtoFunction? = null
for (i in 0 until this.declarationCount) {
val child = this.getDeclaration(i)
if (child.declaratorCase != ProtoDeclaration.DeclaratorCase.IR_FUNCTION) continue
val childFunction = child.irFunction
if (childFunction.base.valueParameterCount != irFunction.valueParameters.size) continue
if (childFunction.base.hasExtensionReceiver() xor (irFunction.extensionReceiverParameter != null)) continue
if (childFunction.base.hasDispatchReceiver() xor (irFunction.dispatchReceiverParameter != null)) continue
if (!FunctionFlags.decode(childFunction.base.base.flags).isInline) continue
val nameAndType = BinaryNameAndType.decode(childFunction.base.nameType)
val name = fileReader.string(nameAndType.nameIndex)
if (name == irFunction.name.asString()) {
if (result == null)
result = childFunction
else {
val resultIdSignature = symbolDeserializer.deserializeIdSignature(BinarySymbolData.decode(result.base.base.symbol).signatureId)
if (resultIdSignature == signature)
return result
result = childFunction
}
}
}
return result ?: error("Function ${irFunction.render()} is not found")
}
object KonanFakeOverrideClassFilter : FakeOverrideClassFilter {
private fun IdSignature.isInteropSignature(): Boolean = with(this) {
@@ -59,7 +303,7 @@ object KonanFakeOverrideClassFilter : FakeOverrideClassFilter {
.any { it.signature?.isInteropSignature() ?: false }
override fun needToConstructFakeOverrides(clazz: IrClass): Boolean {
return !clazz.hasInteropSuperClass()
return !clazz.hasInteropSuperClass() && clazz !is IrLazyClass
}
}
@@ -74,6 +318,7 @@ internal class KonanIrLinker(
private val cenumsProvider: IrProviderForCEnumAndCStructStubs,
exportedDependencies: List<ModuleDescriptor>,
private val cachedLibraries: CachedLibraries,
private val lazyIrForCaches: Boolean,
override val userVisibleIrModulesSupport: UserVisibleIrModulesSupport
) : KotlinIrLinker(currentModule, messageLogger, builtIns, symbolTable, exportedDependencies) {
@@ -92,27 +337,205 @@ internal class KonanIrLinker(
override val fakeOverrideBuilder: FakeOverrideBuilder =
FakeOverrideBuilder(this, symbolTable, KonanManglerIr, IrTypeSystemContextImpl(builtIns), KonanFakeOverrideClassFilter)
override fun createModuleDeserializer(moduleDescriptor: ModuleDescriptor, klib: KotlinLibrary?, strategyResolver: (String) -> DeserializationStrategy): IrModuleDeserializer {
if (moduleDescriptor === forwardModuleDescriptor) {
return forwardDeclarationDeserializer ?: error("forward declaration deserializer expected")
}
val nonCachedLibraryModuleDeserializers = mutableMapOf<ModuleDescriptor, KonanModuleDeserializer>()
val cachedLibraryModuleDeserializers = mutableMapOf<ModuleDescriptor, KonanCachedLibraryModuleDeserializer>()
if (klib != null && klib.isInteropLibrary()) {
// See https://youtrack.jetbrains.com/issue/KT-43517.
// Disabling this flag forces linker to generate IR.
val isCached = false //cachedLibraries.isLibraryCached(klib)
return KonanInteropModuleDeserializer(moduleDescriptor, klib, isCached)
}
override fun createModuleDeserializer(moduleDescriptor: ModuleDescriptor, klib: KotlinLibrary?, strategyResolver: (String) -> DeserializationStrategy) =
when {
moduleDescriptor === forwardModuleDescriptor -> {
forwardDeclarationDeserializer ?: error("forward declaration deserializer expected")
}
klib == null -> {
error("Expecting kotlin library for $moduleDescriptor")
}
klib.isInteropLibrary() -> {
// See https://youtrack.jetbrains.com/issue/KT-43517.
// Disabling this flag forces linker to generate IR.
val isCached = false //cachedLibraries.isLibraryCached(klib)
KonanInteropModuleDeserializer(moduleDescriptor, klib, isCached)
}
lazyIrForCaches && cachedLibraries.isLibraryCached(klib) -> {
KonanCachedLibraryModuleDeserializer(moduleDescriptor, klib).also {
cachedLibraryModuleDeserializers[moduleDescriptor] = it
}
}
else -> {
KonanModuleDeserializer(moduleDescriptor, klib, strategyResolver).also {
nonCachedLibraryModuleDeserializers[moduleDescriptor] = it
}
}
}
return KonanModuleDeserializer(moduleDescriptor, klib ?: error("Expecting kotlin library"), strategyResolver)
override fun postProcess() {
if (lazyIrForCaches)
stubGenerator.unboundSymbolGeneration = true
super.postProcess()
}
private inner class KonanModuleDeserializer(
private val IrClass.firstNonClassParent: IrDeclarationParent
get() {
var parent = parent
while (parent is IrClass) parent = parent.parent
return parent
}
private fun IrClass.getOuterClasses(takeOnlyInner: Boolean): List<IrClass> {
var outerClass = this
val outerClasses = mutableListOf(outerClass)
while (outerClass.isInner || !takeOnlyInner) {
outerClass = outerClass.parent as? IrClass ?: break
outerClasses.add(outerClass)
}
outerClasses.reverse()
return outerClasses
}
private val InvalidIndex = -1
inner class KonanModuleDeserializer(
moduleDescriptor: ModuleDescriptor,
klib: KotlinLibrary,
strategyResolver: (String) -> DeserializationStrategy
): BasicIrModuleDeserializer(this@KonanIrLinker, moduleDescriptor, klib, strategyResolver, klib.versions.abiVersion ?: KotlinAbiVersion.CURRENT) {
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns, emptyList())
fun buildInlineFunctionReference(irFunction: IrFunction): SerializedInlineFunctionReference {
val signature = irFunction.symbol.signature
?: error("No signature for ${irFunction.render()}")
val topLevelSignature = signature.topLevelSignature()
val fileDeserializationState = moduleReversedFileIndex[topLevelSignature]
?: error("No file deserializer for ${topLevelSignature.render()}")
val declarationIndex = fileDeserializationState.fileDeserializer.reversedSignatureIndex[topLevelSignature]
?: error("No declaration for ${topLevelSignature.render()}")
val fileReader = fileDeserializationState.fileReader
val symbolDeserializer = fileDeserializationState.fileDeserializer.symbolDeserializer
val protoDeclaration = fileReader.declaration(declarationIndex)
val outerClasses = (irFunction.parent as? IrClass)?.getOuterClasses(takeOnlyInner = false) ?: emptyList()
require((outerClasses.getOrNull(0)?.parent ?: irFunction.parent) is IrFile) {
"Local inline functions are not supported: ${irFunction.render()}"
}
val typeParameterSigs = mutableListOf<Int>()
val protoFunction = if (outerClasses.isEmpty()) {
val irProperty = (irFunction as? IrSimpleFunction)?.correspondingPropertySymbol?.owner
if (irProperty == null)
protoDeclaration.irFunction
else protoDeclaration.irProperty.findAccessor(irProperty, irFunction)
} else {
val firstNotInnerClassIndex = outerClasses.indexOfLast { !it.isInner }
var protoClass = protoDeclaration.irClass
outerClasses.indices.forEach { classIndex ->
if (classIndex >= firstNotInnerClassIndex /* owner's type parameters are always accessible */) {
(0 until protoClass.typeParameterCount).mapTo(typeParameterSigs) {
BinarySymbolData.decode(protoClass.getTypeParameter(it).base.symbol).signatureId
}
}
if (classIndex < outerClasses.size - 1)
protoClass = protoClass.findClass(outerClasses[classIndex + 1], fileReader, symbolDeserializer)
}
protoClass.findInlineFunction(irFunction, fileReader, symbolDeserializer)
}
val functionSignature = BinarySymbolData.decode(protoFunction.base.base.symbol).signatureId
(0 until protoFunction.base.typeParameterCount).mapTo(typeParameterSigs) {
BinarySymbolData.decode(protoFunction.base.getTypeParameter(it).base.symbol).signatureId
}
val defaultValues = mutableListOf<Int>()
val valueParameterSigs = (0 until protoFunction.base.valueParameterCount).map {
val valueParameter = protoFunction.base.getValueParameter(it)
defaultValues.add(if (valueParameter.hasDefaultValue()) valueParameter.defaultValue else InvalidIndex)
BinarySymbolData.decode(valueParameter.base.symbol).signatureId
}
val extensionReceiverSig = irFunction.extensionReceiverParameter?.let {
BinarySymbolData.decode(protoFunction.base.extensionReceiver.base.symbol).signatureId
} ?: InvalidIndex
val dispatchReceiverSig = irFunction.dispatchReceiverParameter?.let {
BinarySymbolData.decode(protoFunction.base.dispatchReceiver.base.symbol).signatureId
} ?: InvalidIndex
return SerializedInlineFunctionReference(fileDeserializationState.fileIndex, functionSignature, protoFunction.base.body,
irFunction.startOffset, irFunction.endOffset, extensionReceiverSig, dispatchReceiverSig,
valueParameterSigs.toIntArray(), typeParameterSigs.toIntArray(), defaultValues.toIntArray())
}
fun buildClassFields(irClass: IrClass, fields: List<ClassLayoutBuilder.FieldInfo>): SerializedClassFields {
val signature = irClass.symbol.signature
?: error("No signature for ${irClass.render()}")
val topLevelSignature = signature.topLevelSignature()
val fileDeserializationState = moduleReversedFileIndex[topLevelSignature]
?: error("No file deserializer for ${topLevelSignature.render()}")
val fileDeserializer = fileDeserializationState.fileDeserializer
val declarationIndex = fileDeserializer.reversedSignatureIndex[topLevelSignature]
?: error("No declaration for ${topLevelSignature.render()}")
val fileReader = fileDeserializationState.fileReader
val symbolDeserializer = fileDeserializer.symbolDeserializer
val protoDeclaration = fileReader.declaration(declarationIndex)
val outerClasses = irClass.getOuterClasses(takeOnlyInner = false)
require(outerClasses.first().parent is IrFile) { "Local classes are not supported: ${irClass.render()}" }
val typeParameterSigs = mutableListOf<Int>()
var protoClass = protoDeclaration.irClass
val firstNotInnerClassIndex = outerClasses.indexOfLast { !it.isInner }
for (classIndex in outerClasses.indices) {
if (classIndex >= firstNotInnerClassIndex /* owner's type parameters are always accessible */) {
(0 until protoClass.typeParameterCount).mapTo(typeParameterSigs) {
BinarySymbolData.decode(protoClass.getTypeParameter(it).base.symbol).signatureId
}
}
if (classIndex < outerClasses.size - 1)
protoClass = protoClass.findClass(outerClasses[classIndex + 1], fileReader, symbolDeserializer)
}
val protoFields = mutableListOf<ProtoField>()
for (i in 0 until protoClass.declarationCount) {
val declaration = protoClass.getDeclaration(i)
if (declaration.declaratorCase == ProtoDeclaration.DeclaratorCase.IR_FIELD)
protoFields.add(declaration.irField)
else if (declaration.declaratorCase == ProtoDeclaration.DeclaratorCase.IR_PROPERTY) {
val protoProperty = declaration.irProperty
if (protoProperty.hasBackingField())
protoFields.add(protoProperty.backingField)
}
}
val protoFieldsMap = mutableMapOf<String, ProtoField>()
protoFields.forEach {
val nameAndType = BinaryNameAndType.decode(it.nameType)
val name = fileReader.string(nameAndType.nameIndex)
val prev = protoFieldsMap[name]
if (prev != null)
error("Class ${irClass.render()} has two fields with same name '$name'")
protoFieldsMap[name] = it
}
val compatibleMode = CompatibilityMode(libraryAbiVersion).oldSignatures
return SerializedClassFields(
fileDeserializationState.fileIndex,
BinarySymbolData.decode(protoClass.base.symbol).signatureId,
typeParameterSigs.toIntArray(),
Array(fields.size) {
val field = fields[it]
val irField = field.irField ?: error("No IR for field ${field.name} of ${irClass.render()}")
val protoField = protoFieldsMap[field.name] ?: error("No proto for ${irField.render()}")
val nameAndType = BinaryNameAndType.decode(protoField.nameType)
var flags = 0
if (field.isConst)
flags = flags or SerializedClassFieldInfo.FLAG_IS_CONST
if (field.hasConstInitializer)
flags = flags or SerializedClassFieldInfo.FLAG_CONST_INITIALIZER
val classifier = irField.type.classifierOrNull ?: error("Fields of type ${irField.type.render()} are not supported")
val primitiveBinaryType = irField.type.computePrimitiveBinaryTypeOrNull()
SerializedClassFieldInfo(
nameAndType.nameIndex,
primitiveBinaryType?.ordinal ?: InvalidIndex,
if (with(KonanManglerIr) { (classifier as? IrClassSymbol)?.owner?.isExported(compatibleMode) } == false)
InvalidIndex
else nameAndType.typeIndex,
flags)
})
}
}
private inner class KonanInteropModuleDeserializer(
@@ -121,14 +544,15 @@ internal class KonanIrLinker(
private val isLibraryCached: Boolean
) : IrModuleDeserializer(moduleDescriptor, klib.versions.abiVersion ?: KotlinAbiVersion.CURRENT) {
init {
assert(klib.isInteropLibrary())
require(klib.isInteropLibrary())
}
private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinder(
moduleDescriptor, KonanManglerDesc,
DescriptorByIdSignatureFinder.LookupMode.MODULE_ONLY
)
private fun IdSignature.isInteropSignature(): Boolean = IdSignature.Flags.IS_NATIVE_INTEROP_LIBRARY.test()
private fun IdSignature.isInteropSignature() = IdSignature.Flags.IS_NATIVE_INTEROP_LIBRARY.test()
override fun contains(idSig: IdSignature): Boolean {
if (idSig.isPubliclyVisible) {
@@ -170,9 +594,233 @@ internal class KonanIrLinker(
override val moduleDependencies: Collection<IrModuleDeserializer> = listOfNotNull(forwardDeclarationDeserializer)
}
inner class KonanCachedLibraryModuleDeserializer(
moduleDescriptor: ModuleDescriptor,
override val klib: KotlinLibrary
) : IrModuleDeserializer(moduleDescriptor, klib.versions.abiVersion ?: KotlinAbiVersion.CURRENT) {
private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinder(
moduleDescriptor, KonanManglerDesc,
DescriptorByIdSignatureFinder.LookupMode.MODULE_ONLY
)
override fun contains(idSig: IdSignature) =
idSig.isPubliclyVisible && descriptorByIdSignatureFinder.findDescriptorBySignature(idSig) != null
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
val descriptor = descriptorByIdSignatureFinder.findDescriptorBySignature(idSig)
?: error("Expecting descriptor for $idSig")
return (stubGenerator.generateMemberStub(descriptor) as IrSymbolOwner).symbol
}
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns)
override val moduleDependencies by lazy {
moduleDescriptor.allDependencyModules
.filter { it != moduleDescriptor }
.map { resolveModuleDeserializer(it, null) }
}
inner class FileDeserializationInfo(val fileReader: IrLibraryFileFromBytes, val file: IrFile,
val declarationDeserializer: IrDeclarationDeserializer,
val fakeOverrideBuilder: FakeOverrideBuilder)
private val filesDeserializationInfo by lazy {
val result = mutableListOf<FileDeserializationInfo>()
val fileCount = klib.fileCount()
for (i in 0 until fileCount) {
val fileStream = klib.file(i).codedInputStream
val fileProto = ProtoFile.parseFrom(fileStream, ExtensionRegistryLite.newInstance())
val fileReader = IrLibraryFileFromBytes(IrKlibBytesSource(klib, i))
val file = fileReader.createFile(moduleFragment, fileProto)
val symbolDeserializer = IrSymbolDeserializer(
symbolTable, fileReader, file.symbol, emptyList(), { }, { _, symbol -> symbol })
{ idSig, symbolKind ->
val topLevelSig = idSig.topLevelSignature()
val actualModuleDeserializer = resolveModuleDeserializer(moduleDescriptor, null)
.findModuleDeserializerForTopLevelId(topLevelSig)
?: handleSignatureIdNotFoundInModuleWithDependencies(idSig, this)
actualModuleDeserializer.deserializeIrSymbol(idSig, symbolKind)
}
val fakeOverrideBuilder = FakeOverrideBuilder(
object : FileLocalAwareLinker {
override fun tryReferencingSimpleFunctionByLocalSignature(parent: IrDeclaration, idSignature: IdSignature) =
if (idSignature.isPubliclyVisible) null else symbolDeserializer.referenceSimpleFunctionByLocalSignature(idSignature)
override fun tryReferencingPropertyByLocalSignature(parent: IrDeclaration, idSignature: IdSignature) =
if (idSignature.isPubliclyVisible) null else symbolDeserializer.referencePropertyByLocalSignature(idSignature)
},
symbolTable, KonanManglerIr, IrTypeSystemContextImpl(builtIns), KonanFakeOverrideClassFilter)
val declarationDeserializer = IrDeclarationDeserializer(
builtIns,
symbolTable,
IrFactoryImpl,
fileReader,
file,
allowErrorNodes = false,
deserializeInlineFunctions = true,
deserializeBodies = true,
symbolDeserializer,
fakeOverrideBuilder.platformSpecificClassFilter,
fakeOverrideBuilder,
compatibilityMode = CompatibilityMode(libraryAbiVersion)
)
result.add(FileDeserializationInfo(fileReader, file, declarationDeserializer, fakeOverrideBuilder))
}
result
}
private val inlineFunctionReferences by lazy {
cachedLibraries.getLibraryCache(klib)!!.serializedInlineFunctionBodies.associateBy {
filesDeserializationInfo[it.file].declarationDeserializer.symbolDeserializer.deserializeIdSignature(it.functionSignature)
}
}
private val inlineFunctionFiles = mutableMapOf<IrExternalPackageFragment, IrFile>()
fun deserializeInlineFunction(function: IrFunction): InlineFunctionOriginInfo {
val packageFragment = function.findPackage() as? IrExternalPackageFragment
?: error("Expected an external package fragment for ${function.render()}")
if (function.parents.any { (it as? IrFunction)?.isInline == true}) {
// Already deserialized by the top-most inline function.
return InlineFunctionOriginInfo(
inlineFunctionFiles[packageFragment]
?: error("${function.render()} should've been deserialized along with its parent"),
function.startOffset, function.endOffset
)
}
val signature = function.symbol.signature
?: error("No signature for ${function.render()}")
val inlineFunctionReference = inlineFunctionReferences[signature]
?: error("No inline function reference for ${function.render()}, sig = ${signature.render()}")
val fileDeserializationInfo = filesDeserializationInfo[inlineFunctionReference.file]
val declarationDeserializer = fileDeserializationInfo.declarationDeserializer
val symbolDeserializer = declarationDeserializer.symbolDeserializer
val outerClasses = (function.parent as? IrClass)?.getOuterClasses(takeOnlyInner = true) ?: emptyList()
require((outerClasses.getOrNull(0)?.firstNonClassParent ?: function.parent) is IrExternalPackageFragment) {
"Local inline functions are not supported: ${function.render()}"
}
var endToEndTypeParameterIndex = 0
outerClasses.forEach { outerClass ->
outerClass.typeParameters.forEach { parameter ->
val sigIndex = inlineFunctionReference.typeParameterSigs[endToEndTypeParameterIndex++]
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
}
function.typeParameters.forEach { parameter ->
val sigIndex = inlineFunctionReference.typeParameterSigs[endToEndTypeParameterIndex++]
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
function.valueParameters.forEachIndexed { index, parameter ->
val sigIndex = inlineFunctionReference.valueParameterSigs[index]
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
function.extensionReceiverParameter?.let { parameter ->
val sigIndex = inlineFunctionReference.extensionReceiverSig
require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the extension receiver for ${function.render()}" }
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
function.dispatchReceiverParameter?.let { parameter ->
val sigIndex = inlineFunctionReference.dispatchReceiverSig
require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the dispatch receiver for ${function.render()}" }
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
function.body = declarationDeserializer.deserializeStatementBody(inlineFunctionReference.body) as IrBody
function.valueParameters.forEachIndexed { index, parameter ->
val defaultValueIndex = inlineFunctionReference.defaultValues[index]
if (defaultValueIndex != InvalidIndex)
parameter.defaultValue = declarationDeserializer.deserializeExpressionBody(defaultValueIndex)
}
fileDeserializationInfo.fakeOverrideBuilder.provideFakeOverrides()
inlineFunctionFiles[packageFragment]?.let {
require(it == fileDeserializationInfo.file) {
"Different files ${it.fileEntry.name} and ${fileDeserializationInfo.file.fileEntry.name} have the same $packageFragment"
}
}
inlineFunctionFiles[packageFragment] = fileDeserializationInfo.file
return InlineFunctionOriginInfo(fileDeserializationInfo.file, inlineFunctionReference.startOffset, inlineFunctionReference.endOffset)
}
private val classesFields by lazy {
cachedLibraries.getLibraryCache(klib)!!.serializedClassFields.associateBy {
filesDeserializationInfo[it.file].declarationDeserializer.symbolDeserializer.deserializeIdSignature(it.classSignature)
}
}
fun deserializeClassFields(irClass: IrClass): List<ClassLayoutBuilder.FieldInfo> {
val signature = irClass.symbol.signature
?: error("No signature for ${irClass.render()}")
val serializedClassFields = classesFields[signature]
?: error("No class fields for ${irClass.render()}, sig = ${signature.render()}")
val fileDeserializationInfo = filesDeserializationInfo[serializedClassFields.file]
val declarationDeserializer = fileDeserializationInfo.declarationDeserializer
val symbolDeserializer = declarationDeserializer.symbolDeserializer
val outerClasses = irClass.getOuterClasses(takeOnlyInner = true)
require(outerClasses.first().firstNonClassParent is IrExternalPackageFragment) {
"Local classes are not supported: ${irClass.render()}"
}
var endToEndTypeParameterIndex = 0
outerClasses.forEach { outerClass ->
outerClass.typeParameters.forEach { parameter ->
val sigIndex = serializedClassFields.typeParameterSigs[endToEndTypeParameterIndex++]
symbolDeserializer.referenceLocalIrSymbol(parameter.symbol, symbolDeserializer.deserializeIdSignature(sigIndex))
}
}
fun getByClassId(classId: ClassId): IrClassSymbol {
val classIdSig = getPublicSignature(classId.packageFqName, classId.relativeClassName.asString())
return symbolDeserializer.deserializePublicSymbol(classIdSig, BinarySymbolData.SymbolKind.CLASS_SYMBOL) as IrClassSymbol
}
return serializedClassFields.fields.map {
val name = fileDeserializationInfo.fileReader.string(it.name)
val type = when {
it.type != InvalidIndex -> declarationDeserializer.deserializeIrType(it.type)
it.binaryType == InvalidIndex -> builtIns.anyNType
else -> when (PrimitiveBinaryType.values().getOrNull(it.binaryType)) {
PrimitiveBinaryType.BOOLEAN -> builtIns.booleanType
PrimitiveBinaryType.BYTE -> builtIns.byteType
PrimitiveBinaryType.SHORT -> builtIns.shortType
PrimitiveBinaryType.INT -> builtIns.intType
PrimitiveBinaryType.LONG -> builtIns.longType
PrimitiveBinaryType.FLOAT -> builtIns.floatType
PrimitiveBinaryType.DOUBLE -> builtIns.doubleType
PrimitiveBinaryType.POINTER -> getByClassId(KonanPrimitiveType.NON_NULL_NATIVE_PTR.classId).defaultType
PrimitiveBinaryType.VECTOR128 -> getByClassId(KonanPrimitiveType.VECTOR128.classId).defaultType
else -> error("Bad binary type of field $name of ${irClass.render()}")
}
}
ClassLayoutBuilder.FieldInfo(
name, type,
isConst = (it.flags and SerializedClassFieldInfo.FLAG_IS_CONST) != 0,
hasConstInitializer = (it.flags and SerializedClassFieldInfo.FLAG_CONST_INITIALIZER) != 0,
irField = null)
}
}
}
private inner class KonanForwardDeclarationModuleDeserializer(moduleDescriptor: ModuleDescriptor) : IrModuleDeserializer(moduleDescriptor, KotlinAbiVersion.CURRENT) {
init {
assert(moduleDescriptor.isForwardDeclarationModule)
require(moduleDescriptor.isForwardDeclarationModule)
}
private val declaredDeclaration = mutableMapOf<IdSignature, IrClass>()
@@ -190,10 +838,10 @@ internal class KonanIrLinker(
override fun contains(idSig: IdSignature): Boolean = idSig.isForwardDeclarationSignature()
private fun resolveDescriptor(idSig: IdSignature): ClassDescriptor =
with(idSig as IdSignature.CommonSignature) {
val classId = ClassId(packageFqName(), FqName(declarationFqName), false)
moduleDescriptor.findClassAcrossModuleDependencies(classId) ?: error("No declaration found with $idSig")
}
with(idSig as IdSignature.CommonSignature) {
val classId = ClassId(packageFqName(), FqName(declarationFqName), false)
moduleDescriptor.findClassAcrossModuleDependencies(classId) ?: error("No declaration found with $idSig")
}
private fun buildForwardDeclarationStub(descriptor: ClassDescriptor): IrClass {
return stubGenerator.generateClassStub(descriptor).also {
@@ -202,7 +850,9 @@ internal class KonanIrLinker(
}
override fun deserializeIrSymbol(idSig: IdSignature, symbolKind: BinarySymbolData.SymbolKind): IrSymbol {
assert(symbolKind == BinarySymbolData.SymbolKind.CLASS_SYMBOL) { "Only class could be a Forward declaration $idSig (kind $symbolKind)" }
require(symbolKind == BinarySymbolData.SymbolKind.CLASS_SYMBOL) {
"Only class could be a Forward declaration $idSig (kind $symbolKind)"
}
val descriptor = resolveDescriptor(idSig)
val actualModule = descriptor.module
if (actualModule !== moduleDescriptor) {