From 0cece79856685d201d6ca77a3fcac2d9d6207593 Mon Sep 17 00:00:00 2001 From: Igor Chevdar Date: Tue, 24 Aug 2021 21:14:52 +0500 Subject: [PATCH] [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 --- .../common/lower/inline/FunctionInlining.kt | 2 +- .../ir/declarations/lazy/IrLazyClass.kt | 4 +- .../ir/declarations/lazy/IrLazyConstructor.kt | 33 + .../ir/declarations/lazy/IrLazySymbolTable.kt | 25 +- .../kotlin/ir/expressions/IrBlock.kt | 5 +- .../kotlin/ir/overrides/IrOverridingUtil.kt | 24 +- .../ir/util/DeclarationStubGenerator.kt | 9 +- .../jetbrains/kotlin/ir/util/SymbolTable.kt | 6 + .../BasicIrModuleDeserializer.kt | 3 +- .../serialization/IrFileDeserializer.kt | 3 +- .../serialization/IrModuleDeserializer.kt | 1 + .../stubs/constFromBuiltins__kotlin.Int.txt | 1 + .../org/jetbrains/kotlin/cli/bc/K2Native.kt | 9 + .../cli/bc/K2NativeCompilerArguments.kt | 3 + .../jetbrains/kotlin/backend/konan/Boxing.kt | 8 +- ...BuiltInFictitiousFunctionIrClassFactory.kt | 93 ++- .../kotlin/backend/konan/CachedLibraries.kt | 17 + .../jetbrains/kotlin/backend/konan/Context.kt | 13 +- .../kotlin/backend/konan/EntryPoint.kt | 24 +- .../kotlin/backend/konan/KonanConfig.kt | 2 + .../backend/konan/KonanConfigurationKeys.kt | 1 + .../jetbrains/kotlin/backend/konan/Linker.kt | 16 + .../kotlin/backend/konan/OutputFiles.kt | 10 + .../jetbrains/kotlin/backend/konan/PsiToIr.kt | 43 +- .../kotlin/backend/konan/ToplevelPhases.kt | 63 +- .../konan/descriptors/ClassLayoutBuilder.kt | 14 + .../kotlin/backend/konan/ir/FakeIrUtils.kt | 8 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 16 +- .../lower/NativeInlineFunctionResolver.kt | 25 +- .../backend/konan/lower/PreInlineLowering.kt | 7 +- .../konan/serialization/KonanIrlinker.kt | 694 +++++++++++++++++- 31 files changed, 1042 insertions(+), 140 deletions(-) diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt index d4a938bb068..213d29e48fe 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/inline/FunctionInlining.kt @@ -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). */ diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyClass.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyClass.kt index 902e7429782..a87968c7516 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyClass.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyClass.kt @@ -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 by lazyVar(stubGenerator.lock) { descriptor.declaredTypeParameters.mapTo(arrayListOf()) { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyConstructor.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyConstructor.kt index 1a833efa03d..f1c748d24a3 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyConstructor.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazyConstructor.kt @@ -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 by createLazyAnnotations() + + init { + symbol.bind(this) + } +} + @OptIn(ObsoleteDescriptorBasedAPI::class) class IrLazyConstructor( override val startOffset: Int, diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazySymbolTable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazySymbolTable.kt index 82e4343d6bc..cdfd9189632 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazySymbolTable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/declarations/lazy/IrLazySymbolTable.kt @@ -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 { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt index 0eba2a78492..f4fd9b9820e 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/expressions/IrBlock.kt @@ -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 diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt index c69c335a896..6359178b114 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/overrides/IrOverridingUtil.kt @@ -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 { - 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( diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt index 2e999c96029..e284d07275b 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/DeclarationStubGenerator.kt @@ -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 } diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt index e3983043389..50806ac05e8 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/SymbolTable.kt @@ -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() diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt index 18ab4ea3d14..f59ad2f01f3 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/BasicIrModuleDeserializer.kt @@ -35,7 +35,7 @@ abstract class BasicIrModuleDeserializer( private val moduleDeserializationState = ModuleDeserializationState(linker, this) - internal val moduleReversedFileIndex = mutableMapOf() + val moduleReversedFileIndex = mutableMapOf() 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, diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt index a602ba23966..8370b7f4242 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrFileDeserializer.kt @@ -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, diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt index 9fadf090693..d5bc72e2561 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrModuleDeserializer.kt @@ -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") } } diff --git a/compiler/testData/ir/irText/stubs/constFromBuiltins__kotlin.Int.txt b/compiler/testData/ir/irText/stubs/constFromBuiltins__kotlin.Int.txt index 8c0cab20869..a58479d46f2 100644 --- a/compiler/testData/ir/irText/stubs/constFromBuiltins__kotlin.Int.txt +++ b/compiler/testData/ir/irText/stubs/constFromBuiltins__kotlin.Int.txt @@ -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:[] $this: VALUE_PARAMETER IR_EXTERNAL_DECLARATION_STUB name: 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 diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index f821ba49521..71f734d4f07 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -364,6 +364,15 @@ class K2Native : CLICompiler() { 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) { diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt index 08bc03c41f7..bd563f79040 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2NativeCompilerArguments.kt @@ -361,6 +361,9 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xruntime-logs", valueDescription = "", 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, Any> = super.configureAnalysisFlags(collector, languageVersion).also { val optInList = it[AnalysisFlags.optIn] as List<*> diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt index f86db1d82b7..5d3d0efddf0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt @@ -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 } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt index 031e81aa8d6..c15774ab1d3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BuiltInFictitiousFunctionIrClassFactory.kt @@ -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() + + 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) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt index 1f208e9e82b..4411ef3af78 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CachedLibraries.kt @@ -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>() @@ -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" } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 07bfed09557..0d3931e7090 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -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() - val loweredInlineFunctions = mutableSetOf() + val loweredInlineFunctions = mutableMapOf() 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() + + val classFields = mutableListOf() } private fun MemberScope.getContributedClassifier(name: String) = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EntryPoint.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EntryPoint.kt index 723d5cb78ab..079c40bad20 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EntryPoint.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/EntryPoint.kt @@ -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
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) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 01d5588da11..a23569e9238 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -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) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index 3704d6f842c..b3647358c38 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -170,6 +170,7 @@ class KonanConfigKeys { CompilerConfigurationKey.create("use external dependencies to enhance IR linker error messages") val LLVM_VARIANT: CompilerConfigurationKey = CompilerConfigurationKey.create("llvm variant") val RUNTIME_LOGS: CompilerConfigurationKey = CompilerConfigurationKey.create("enable runtime logging") + val LAZY_IR_FOR_CACHES: CompilerConfigurationKey = CompilerConfigurationKey.create("use lazy IR for cached libraries") } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt index d719daf5f0f..a6b2a8c1b55 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -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 diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt index 14ba4aa8c31..dbd04f60484 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OutputFiles.kt @@ -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) = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt index 089cd055036..c69e4f4866d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/PsiToIr.kt @@ -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) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index b07e09ce366..dde62f9f145 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -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( 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) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt index f7657f82d03..10d402adcb2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/ClassLayoutBuilder.kt @@ -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 { + 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 = if (irClass.isInner && !isLowered) { // Note: copying to avoid mutation of the original class. irClass.declarations.toMutableList() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt index 5dd514db789..819c37ab551 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/FakeIrUtils.kt @@ -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() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 8d0b66b91d5..404e3c0692f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -1824,15 +1824,23 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map { 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) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index f8663482540..63a25990d2f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -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): 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 { + val result = mutableListOf() + 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) + +internal object ClassFieldsSerializer { + fun serialize(classFields: List): 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 { + val result = mutableListOf() + 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, 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() + val cachedLibraryModuleDeserializers = mutableMapOf() - 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 { + 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() + 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() + 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): 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() + 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() + 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() + 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 = 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() + 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() + + 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 { + 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() @@ -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) {