diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt index eed4e2748d0..f018997747a 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/IrDeclarationDeserializer.kt @@ -577,6 +577,16 @@ class IrDeclarationDeserializer( } } + fun T.withDeserializeBodies(block: T.() -> Unit) { + val oldBodiesPolicy = deserializeBodies + try { + deserializeBodies = true + usingParent { block() } + } finally { + deserializeBodies = oldBodiesPolicy + } + } + internal fun deserializeIrFunction(proto: ProtoFunction, setParent: Boolean = true): IrSimpleFunction = withDeserializedIrFunctionBase( proto.base, diff --git a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt index 2bc94c71719..f34dd071a6e 100644 --- a/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt +++ b/compiler/ir/serialization.common/src/org/jetbrains/kotlin/backend/common/serialization/KotlinIrLinker.kt @@ -102,7 +102,7 @@ abstract class KotlinIrLinker( protected abstract fun isBuiltInModule(moduleDescriptor: ModuleDescriptor): Boolean - private fun deserializeAllReachableTopLevels() { + fun deserializeAllReachableTopLevels() { while (modulesWithReachableTopLevels.isNotEmpty()) { val moduleDeserializer = modulesWithReachableTopLevels.first() modulesWithReachableTopLevels.remove(moduleDeserializer) 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 e19a9570e9c..6c7c3cbf43e 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 @@ -175,10 +175,8 @@ internal fun PsiToIrContext.psiToIr( val kotlinLibrary = (dependency.getCapability(KlibModuleOrigin.CAPABILITY) as? DeserializedKlibModuleOrigin)?.library val isFullyCachedLibrary = kotlinLibrary != null && config.cachedLibraries.isLibraryCached(kotlinLibrary) && kotlinLibrary != config.libraryToCache?.klib - if (isProducingLibrary || (config.lazyIrForCaches && isFullyCachedLibrary)) + if (isProducingLibrary || isFullyCachedLibrary) linker.deserializeOnlyHeaderModule(dependency, kotlinLibrary) - else if (isFullyCachedLibrary) - linker.deserializeHeadersWithInlineBodies(dependency, kotlinLibrary!!) else linker.deserializeIrModuleHeader(dependency, kotlinLibrary, dependency.name.asString()) } 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 0703696aea0..de509287abd 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 @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.computeFunctionName import org.jetbrains.kotlin.backend.konan.llvm.toLLVMType import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.backend.konan.lower.bridgeTarget +import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* @@ -507,17 +508,10 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { val outerThisField = if (irClass.isInner) context.innerClassesSupport.getOuterThisField(irClass) else null - val packageFragment = irClass.getPackageFragment() - if (packageFragment is IrExternalPackageFragment) { - val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration - if (moduleDescriptor.isFromInteropLibrary()) return emptyList() - val moduleDeserializer = context.irLinker.moduleDeserializers[moduleDescriptor] - ?: error("No module deserializer for ${irClass.render()}") - require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { - "No IR and no cache for ${irClass.render()}" - } + + val moduleDeserializer = context.irLinker.getCachedDeclarationModuleDeserializer(irClass) + if (moduleDeserializer != null) return moduleDeserializer.deserializeClassFields(irClass, outerThisField?.toFieldInfo(llvm)) - } val declarations = irClass.declarations.toMutableList() outerThisField?.let { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt index f5f8585a5b4..35d18882a04 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/CacheInfoBuilder.kt @@ -93,7 +93,7 @@ internal class CacheInfoBuilder( } private fun processFunction(function: IrFunction) { - if (function.getPackageFragment() !is IrExternalPackageFragment) { + if (generationState.context.irLinker.getCachedDeclarationModuleDeserializer(function) == null) { generationState.calledFromExportedInlineFunctions.add(function) (function as? IrConstructor)?.constructedClass?.let { generationState.constructedFromExportedInlineFunctions.add(it) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt index d8d6cb5af0e..09450a979f1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/NativeInlineFunctionResolver.kt @@ -9,7 +9,7 @@ import org.jetbrains.kotlin.backend.common.DeclarationTransformer import org.jetbrains.kotlin.backend.common.lower.* import org.jetbrains.kotlin.backend.common.lower.inline.* import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment +import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.deepCopyWithVariables @@ -38,9 +38,15 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g generationState.inlineFunctionOrigins[function]?.let { return it.irFunction } val packageFragment = function.getPackageFragment() - val functionIsNotFromLazyIr = packageFragment !is IrExternalPackageFragment + val moduleDeserializer = context.irLinker.getCachedDeclarationModuleDeserializer(function) val irFile: IrFile - val (possiblyLoweredFunction, shouldLower) = if (functionIsNotFromLazyIr) { + val (possiblyLoweredFunction, shouldLower) = if (moduleDeserializer != null) { + // The function is cached, get its body from the IR linker. + val (firstAccess, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function) + generationState.inlineFunctionOrigins[function] = deserializedInlineFunction + irFile = deserializedInlineFunction.irFile + function to firstAccess + } else { irFile = packageFragment as IrFile val partiallyLoweredFunction = context.inlineFunctionsSupport.getPartiallyLoweredInlineFunction(function) if (partiallyLoweredFunction == null) @@ -50,23 +56,12 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g InlineFunctionOriginInfo(partiallyLoweredFunction, irFile, function.startOffset, function.endOffset) partiallyLoweredFunction to false } - } else { - // The function is from Lazy IR, get its body from the IR linker. - val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration - val moduleDeserializer = context.irLinker.moduleDeserializers[moduleDescriptor] - ?: error("No module deserializer for ${function.render()}") - require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { - "No IR and no cache for ${function.render()}" - } - val (firstAccess, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function) - generationState.inlineFunctionOrigins[function] = deserializedInlineFunction - irFile = deserializedInlineFunction.irFile - function to firstAccess } if (shouldLower) { - lower(possiblyLoweredFunction, irFile, functionIsNotFromLazyIr) - if (functionIsNotFromLazyIr) { + val functionIsCached = moduleDeserializer != null + lower(possiblyLoweredFunction, irFile, functionIsCached) + if (!functionIsCached) { generationState.inlineFunctionOrigins[function] = InlineFunctionOriginInfo(context.inlineFunctionsSupport.savePartiallyLoweredInlineFunction(possiblyLoweredFunction), irFile, function.startOffset, function.endOffset) @@ -75,7 +70,7 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g return possiblyLoweredFunction } - private fun lower(function: IrFunction, irFile: IrFile, functionIsNotFromLazyIr: Boolean) { + private fun lower(function: IrFunction, irFile: IrFile, functionIsCached: Boolean) { val body = function.body ?: return PreInlineLowering(context).lower(body, function, irFile) @@ -92,7 +87,7 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g LocalClassesInInlineLambdasLowering(context).lower(body, function) - if (!context.config.produce.isCache && functionIsNotFromLazyIr) { + if (!(context.config.produce.isCache || functionIsCached)) { // Do not extract local classes off of inline functions from cached libraries. LocalClassesInInlineFunctionsLowering(context).lower(body, function) LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, function) 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 a5f6910ac57..58a4a2c9708 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 @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.backend.konan.descriptors.findPackage import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs +import org.jetbrains.kotlin.backend.konan.ir.konanLibrary import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.fir.lazy.Fir2IrLazyClass @@ -491,6 +492,18 @@ internal class KonanIrLinker( val moduleDeserializers = mutableMapOf() val klibToModuleDeserializerMap = mutableMapOf() + fun getCachedDeclarationModuleDeserializer(declaration: IrDeclaration): KonanPartialModuleDeserializer? { + val packageFragment = declaration.getPackageFragment() + val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration + val klib = packageFragment.konanLibrary + val declarationBeingCached = packageFragment is IrFile && klib != null && libraryBeingCached?.klib == klib + && libraryBeingCached.strategy.contains(packageFragment.path) + return if (klib != null && !moduleDescriptor.isFromInteropLibrary() + && cachedLibraries.isLibraryCached(klib) && !declarationBeingCached) + moduleDeserializers[moduleDescriptor] ?: error("No module deserializer for ${declaration.render()}") + else null + } + override fun createModuleDeserializer(moduleDescriptor: ModuleDescriptor, klib: KotlinLibrary?, strategyResolver: (String) -> DeserializationStrategy) = when { moduleDescriptor === forwardModuleDescriptor -> { @@ -839,76 +852,84 @@ internal class KonanIrLinker( } private fun deserializeInlineFunctionInternal(function: IrFunction): InlineFunctionOriginInfo { - val packageFragment = function.getPackageFragment() as? IrExternalPackageFragment - ?: error("Expected an external package fragment for ${function.render()}") + val packageFragment = function.getPackageFragment() if (function.parents.any { (it as? IrFunction)?.isInline == true }) { // Already deserialized by the top-most inline function. return InlineFunctionOriginInfo( function, - inlineFunctionFiles[packageFragment] + packageFragment as? IrFile + ?: inlineFunctionFiles[packageFragment as IrExternalPackageFragment] ?: error("${function.render()} should've been deserialized along with its parent"), function.startOffset, function.endOffset ) } - val signature = function.symbol.signature ?: descriptorSignatures[function.descriptor] - ?: error("No signature for ${function.render()}") + val signature = function.symbol.signature + ?: descriptorSignatures[function.descriptor] + ?: error("No signature for ${function.render()}") val inlineFunctionReference = inlineFunctionReferences[signature] ?: error("No inline function reference for ${function.render()}, sig = ${signature.render()}") val fileDeserializationState = inlineFunctionReference.file.deserializationState val declarationDeserializer = fileDeserializationState.declarationDeserializer - val symbolDeserializer = declarationDeserializer.symbolDeserializer - inlineFunctionFiles[packageFragment]?.let { - require(it == fileDeserializationState.file) { - "Different files ${it.fileEntry.name} and ${fileDeserializationState.file.fileEntry.name} have the same $packageFragment" + if (packageFragment is IrExternalPackageFragment) { + val symbolDeserializer = declarationDeserializer.symbolDeserializer + + inlineFunctionFiles[packageFragment]?.let { + require(it == fileDeserializationState.file) { + "Different files ${it.fileEntry.name} and ${fileDeserializationState.file.fileEntry.name} have the same $packageFragment" + } } - } - inlineFunctionFiles[packageFragment] = fileDeserializationState.file + inlineFunctionFiles[packageFragment] = fileDeserializationState.file - 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()}" - } + val outerClasses = (function.parent as? IrClass)?.getOuterClasses(takeOnlyInner = true) ?: emptyList() + require((outerClasses.getOrNull(0)?.firstNonClassParent ?: function.parent) is IrPackageFragment) { + "Local inline functions are not supported: ${function.render()}" + } - var endToEndTypeParameterIndex = 0 - outerClasses.forEach { outerClass -> - outerClass.typeParameters.forEach { parameter -> + var endToEndTypeParameterIndex = 0 + outerClasses.forEach { outerClass -> + outerClass.typeParameters.forEach { parameter -> + val sigIndex = inlineFunctionReference.typeParameterSigs[endToEndTypeParameterIndex++] + referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + } + } + function.typeParameters.forEach { parameter -> val sigIndex = inlineFunctionReference.typeParameterSigs[endToEndTypeParameterIndex++] referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) } - } - function.typeParameters.forEach { parameter -> - val sigIndex = inlineFunctionReference.typeParameterSigs[endToEndTypeParameterIndex++] - referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) - } - function.valueParameters.forEachIndexed { index, parameter -> - val sigIndex = inlineFunctionReference.valueParameterSigs[index] - referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) - } - function.extensionReceiverParameter?.let { parameter -> - val sigIndex = inlineFunctionReference.extensionReceiverSig - require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the extension receiver for ${function.render()}" } - referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) - } - function.dispatchReceiverParameter?.let { parameter -> - val sigIndex = inlineFunctionReference.dispatchReceiverSig - require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the dispatch receiver for ${function.render()}" } - referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) - } - for (index in 0 until outerClasses.size - 1) { - val sigIndex = inlineFunctionReference.outerReceiverSigs[index] - referenceIrSymbol(symbolDeserializer, sigIndex, outerClasses[index].thisReceiver!!.symbol) + function.valueParameters.forEachIndexed { index, parameter -> + val sigIndex = inlineFunctionReference.valueParameterSigs[index] + referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + } + function.extensionReceiverParameter?.let { parameter -> + val sigIndex = inlineFunctionReference.extensionReceiverSig + require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the extension receiver for ${function.render()}" } + referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + } + function.dispatchReceiverParameter?.let { parameter -> + val sigIndex = inlineFunctionReference.dispatchReceiverSig + require(sigIndex != InvalidIndex) { "Expected a valid sig reference to the dispatch receiver for ${function.render()}" } + referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + } + for (index in 0 until outerClasses.size - 1) { + val sigIndex = inlineFunctionReference.outerReceiverSigs[index] + referenceIrSymbol(symbolDeserializer, sigIndex, outerClasses[index].thisReceiver!!.symbol) + } } with(declarationDeserializer) { - function.body = (deserializeStatementBody(inlineFunctionReference.body) as IrBody).setDeclarationsParent(function) - function.valueParameters.forEachIndexed { index, parameter -> - val defaultValueIndex = inlineFunctionReference.defaultValues[index] - if (defaultValueIndex != InvalidIndex) - parameter.defaultValue = deserializeExpressionBody(defaultValueIndex)?.setDeclarationsParent(function) + function.withDeserializeBodies { + body = (deserializeStatementBody(inlineFunctionReference.body) as IrBody) + valueParameters.forEachIndexed { index, parameter -> + val defaultValueIndex = inlineFunctionReference.defaultValues[index] + if (defaultValueIndex != InvalidIndex) + parameter.defaultValue = deserializeExpressionBody(defaultValueIndex) + } } } + if (packageFragment is IrFile) + deserializeAllReachableTopLevels() partialLinkageSupport.exploreClassifiers(fakeOverrideBuilder) partialLinkageSupport.exploreClassifiersInInlineLazyIrFunction(function) @@ -935,8 +956,6 @@ internal class KonanIrLinker( private val lock = Any() fun deserializeClassFields(irClass: IrClass, outerThisFieldInfo: ClassLayoutBuilder.FieldInfo?): List = synchronized(lock) { - irClass.getPackageFragment() as? IrExternalPackageFragment - ?: error("Expected an external package fragment for ${irClass.render()}") val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}") val serializedClassFields = classesFields[signature] @@ -945,20 +964,22 @@ internal class KonanIrLinker( val declarationDeserializer = fileDeserializationState.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++] - referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + if (irClass.getPackageFragment() is IrExternalPackageFragment) { + 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++] + referenceIrSymbol(symbolDeserializer, sigIndex, parameter.symbol) + } + } + require(endToEndTypeParameterIndex == serializedClassFields.typeParameterSigs.size) { + "Not all type parameters have been referenced" } - } - require(endToEndTypeParameterIndex == serializedClassFields.typeParameterSigs.size) { - "Not all type parameters have been referenced" } fun getByClassId(classId: ClassId): IrClassSymbol {