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 17980289499..dcfe493bc3e 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 @@ -13,41 +13,24 @@ import org.jetbrains.kotlin.backend.konan.cexport.CAdapterExportedElements import org.jetbrains.kotlin.backend.konan.descriptors.BridgeDirections import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysisResult -import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrContext -import org.jetbrains.kotlin.backend.konan.driver.phases.PsiToIrOutput import org.jetbrains.kotlin.backend.konan.ir.KonanIr import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.llvm.CodegenClassMetadata -import org.jetbrains.kotlin.backend.konan.llvm.Lifetime -import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.backend.konan.lower.* import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportCodeSpec import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExportedInterface -import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis -import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns -import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.ModuleDescriptor -import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor import org.jetbrains.kotlin.ir.IrBuiltIns -import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.lazy.IrLazyClass import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol -import org.jetbrains.kotlin.ir.symbols.IrSymbol import org.jetbrains.kotlin.ir.types.IrTypeSystemContext import org.jetbrains.kotlin.ir.types.IrTypeSystemContextImpl -import org.jetbrains.kotlin.ir.util.SymbolTable -import org.jetbrains.kotlin.konan.library.KonanLibraryLayout -import org.jetbrains.kotlin.konan.target.Architecture -import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name -import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.descriptorUtil.module -import kotlin.LazyThreadSafetyMode.PUBLICATION internal class NativeMapping : DefaultMapping() { data class BridgeKey(val target: IrSimpleFunction, val bridgeDirections: BridgeDirections) @@ -60,7 +43,7 @@ internal class NativeMapping : DefaultMapping() { val enumValueGetters = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val enumEntriesMaps = mutableMapOf>() val bridges = mutableMapOf() - val notLoweredInlineFunctions = mutableMapOf() + val partiallyLoweredInlineFunctions = mutableMapOf() val outerThisCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val lateinitPropertyCacheAccessors = DefaultDelegateFactory.newDeclarationToDeclarationMapping() val objectInstanceGetter = DefaultDelegateFactory.newDeclarationToDeclarationMapping() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt index a3d60ade008..04d0533d3c1 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/NativeGenerationState.kt @@ -69,7 +69,7 @@ internal class NativeGenerationState( val eagerInitializedFiles = mutableListOf() val calledFromExportedInlineFunctions = mutableSetOf() val constructedFromExportedInlineFunctions = mutableSetOf() - val loweredInlineFunctions = mutableMapOf() + val inlineFunctionOrigins = mutableMapOf() private val localClassNames = mutableMapOf() fun getLocalClassName(container: IrAttributeContainer): String? = localClassNames[container.attributeOwnerId] diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt index 28e5c7d4ae5..a523b6b170c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt @@ -33,7 +33,6 @@ import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer -import org.jetbrains.kotlin.ir.util.isLocal import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid @@ -105,7 +104,7 @@ private val sharedVariablesPhase = createFileLoweringPhase( prerequisite = setOf(lateinitPhase) ) -private val extractLocalClassesFromInlineBodies = createFileLoweringPhase( +private val lowerOuterThisInInlineFunctionsPhase = createFileLoweringPhase( { context, irFile -> irFile.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { @@ -113,15 +112,24 @@ private val extractLocalClassesFromInlineBodies = createFileLoweringPhase( } override fun visitFunction(declaration: IrFunction) { - if (declaration.isInline && !declaration.isLocal) - context.inlineFunctionsSupport.saveNonLoweredInlineFunction(declaration) declaration.acceptChildrenVoid(this) + + if (declaration.isInline) + OuterThisLowering(context).lower(declaration) } }) + }, + name = "LowerOuterThisInInlineFunctions", + description = "Lower outer this in inline functions" +) +private val extractLocalClassesFromInlineBodies = createFileLoweringPhase( + { context, irFile -> LocalClassesInInlineLambdasLowering(context).lower(irFile) - LocalClassesInInlineFunctionsLowering(context).lower(irFile) - LocalClassesExtractionFromInlineFunctionsLowering(context).lower(irFile) + if (!context.config.produce.isCache) { + LocalClassesInInlineFunctionsLowering(context).lower(irFile) + LocalClassesExtractionFromInlineFunctionsLowering(context).lower(irFile) + } }, name = "ExtractLocalClassesFromInlineBodies", description = "Extraction of local classes from inline bodies", @@ -322,6 +330,18 @@ private val inlinePhase = createFileLoweringPhase( lowering = { context: NativeGenerationState -> object : FileLoweringPass { override fun lower(irFile: IrFile) { + irFile.acceptChildrenVoid(object : IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitFunction(declaration: IrFunction) { + if (declaration.isInline) + context.context.inlineFunctionsSupport.savePartiallyLoweredInlineFunction(declaration) + declaration.acceptChildrenVoid(this) + } + }) + FunctionInlining( context.context, NativeInlineFunctionResolver(context.context, context), @@ -493,6 +513,7 @@ private fun PhaseEngine.getAllLowerings() = listOfNotNull arrayConstructorPhase, lateinitPhase, sharedVariablesPhase, + lowerOuterThisInInlineFunctionsPhase, inventNamesForLocalClasses, extractLocalClassesFromInlineBodies, wrapInlineDeclarationsWithReifiedTypeParametersLowering, 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 3a9cb48a9cb..801368cf482 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 @@ -1960,7 +1960,7 @@ internal class CodeGeneratorVisitor( //-------------------------------------------------------------------------// private inner class ReturnableBlockScope(val returnableBlock: IrReturnableBlock, val resultSlot: LLVMValueRef?) : FileScope(returnableBlock.inlineFunctionSymbol?.owner?.let { - generationState.loweredInlineFunctions[it]?.irFile ?: it.fileOrNull + generationState.inlineFunctionOrigins[it]?.irFile ?: it.fileOrNull } ?: (currentCodeContext.fileScope() as? FileScope)?.file ?: error("returnable block should belong to current file at least")) { @@ -1969,13 +1969,13 @@ internal class CodeGeneratorVisitor( var resultPhi : LLVMValueRef? = null private val functionScope by lazy { returnableBlock.inlineFunctionSymbol?.owner?.let { - it.scope(file().fileEntry.line(generationState.loweredInlineFunctions[it]?.startOffset ?: it.startOffset)) + it.scope(file().fileEntry.line(generationState.inlineFunctionOrigins[it]?.startOffset ?: it.startOffset)) } } private fun getExit(): LLVMBasicBlockRef { val location = returnableBlock.inlineFunctionSymbol?.owner?.let { - location(generationState.loweredInlineFunctions[it]?.endOffset ?: it.endOffset) + location(generationState.inlineFunctionOrigins[it]?.endOffset ?: it.endOffset) } ?: returnableBlock.statements.lastOrNull()?.let { location(it.endOffset) } 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 e9afeaca65f..d8d6cb5af0e 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 @@ -8,10 +8,7 @@ package org.jetbrains.kotlin.backend.konan.lower 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.Context -import org.jetbrains.kotlin.backend.konan.InlineFunctionOriginInfo -import org.jetbrains.kotlin.backend.konan.NativeGenerationState -import org.jetbrains.kotlin.backend.konan.NativeMapping +import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.ir.declarations.IrExternalPackageFragment import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction @@ -20,21 +17,17 @@ import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.util.* internal class InlineFunctionsSupport(mapping: NativeMapping) { - private val notLoweredInlineFunctions = mapping.notLoweredInlineFunctions + // Inline functions lowered up to just before the inliner. + private val partiallyLoweredInlineFunctions = mapping.partiallyLoweredInlineFunctions - fun saveNonLoweredInlineFunction(function: IrFunction) { - getNonLoweredInlineFunction(function, copy = false) - } + fun savePartiallyLoweredInlineFunction(function: IrFunction) = + function.deepCopyWithVariables().also { + it.patchDeclarationParents(function.parent) + partiallyLoweredInlineFunctions[function.symbol] = it + } - fun getNonLoweredInlineFunction(function: IrFunction, copy: Boolean): IrFunction { - val notLoweredInlineFunction = notLoweredInlineFunctions.getOrPut(function.symbol) { - function.deepCopyWithVariables().also { it.patchDeclarationParents(function.parent) } - } - return if (copy) - notLoweredInlineFunction.deepCopyWithVariables().also { it.patchDeclarationParents(function.parent) } - else - notLoweredInlineFunction - } + fun getPartiallyLoweredInlineFunction(function: IrFunction) = + partiallyLoweredInlineFunctions[function.symbol] } // TODO: This is a bit hacky. Think about adopting persistent IR ideas. @@ -42,14 +35,21 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g override fun getFunctionDeclaration(symbol: IrFunctionSymbol): IrFunction { val function = super.getFunctionDeclaration(symbol) - generationState.loweredInlineFunctions[function]?.let { return it.irFunction } + generationState.inlineFunctionOrigins[function]?.let { return it.irFunction } val packageFragment = function.getPackageFragment() - val (possiblyLoweredFunction, shouldLower) = if (packageFragment !is IrExternalPackageFragment) { - context.inlineFunctionsSupport.getNonLoweredInlineFunction(function, copy = context.config.producePerFileCache).also { - generationState.loweredInlineFunctions[function] = - InlineFunctionOriginInfo(it, packageFragment as IrFile, function.startOffset, function.endOffset) - } to true + val functionIsNotFromLazyIr = packageFragment !is IrExternalPackageFragment + val irFile: IrFile + val (possiblyLoweredFunction, shouldLower) = if (functionIsNotFromLazyIr) { + irFile = packageFragment as IrFile + val partiallyLoweredFunction = context.inlineFunctionsSupport.getPartiallyLoweredInlineFunction(function) + if (partiallyLoweredFunction == null) + function to true + else { + generationState.inlineFunctionOrigins[function] = + 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 @@ -58,40 +58,49 @@ internal class NativeInlineFunctionResolver(override val context: Context, val g require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { "No IR and no cache for ${function.render()}" } - val (shouldLower, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function) - generationState.loweredInlineFunctions[function] = deserializedInlineFunction - function to shouldLower + val (firstAccess, deserializedInlineFunction) = moduleDeserializer.deserializeInlineFunction(function) + generationState.inlineFunctionOrigins[function] = deserializedInlineFunction + irFile = deserializedInlineFunction.irFile + function to firstAccess } - if (!shouldLower) return possiblyLoweredFunction - - val body = possiblyLoweredFunction.body ?: return possiblyLoweredFunction - - PreInlineLowering(context).lower(body, possiblyLoweredFunction, generationState.loweredInlineFunctions[function]!!.irFile) - - ArrayConstructorLowering(context).lower(body, possiblyLoweredFunction) - - NullableFieldsForLateinitCreationLowering(context).lowerWithLocalDeclarations(possiblyLoweredFunction) - NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(possiblyLoweredFunction) - LateinitUsageLowering(context).lower(body, possiblyLoweredFunction) - - SharedVariablesLowering(context).lower(body, possiblyLoweredFunction) - - OuterThisLowering(context).lower(possiblyLoweredFunction) - - LocalClassesInInlineLambdasLowering(context).lower(body, possiblyLoweredFunction) - - if (generationState.llvmModuleSpecification.containsDeclaration(function)) { - // Do not extract local classes off of inline functions from cached libraries. - LocalClassesInInlineFunctionsLowering(context).lower(body, possiblyLoweredFunction) - LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, possiblyLoweredFunction) + if (shouldLower) { + lower(possiblyLoweredFunction, irFile, functionIsNotFromLazyIr) + if (functionIsNotFromLazyIr) { + generationState.inlineFunctionOrigins[function] = + InlineFunctionOriginInfo(context.inlineFunctionsSupport.savePartiallyLoweredInlineFunction(possiblyLoweredFunction), + irFile, function.startOffset, function.endOffset) + } } - - WrapInlineDeclarationsWithReifiedTypeParametersLowering(context).lower(body, possiblyLoweredFunction) - return possiblyLoweredFunction } + private fun lower(function: IrFunction, irFile: IrFile, functionIsNotFromLazyIr: Boolean) { + val body = function.body ?: return + + PreInlineLowering(context).lower(body, function, irFile) + + ArrayConstructorLowering(context).lower(body, function) + + NullableFieldsForLateinitCreationLowering(context).lowerWithLocalDeclarations(function) + NullableFieldsDeclarationLowering(context).lowerWithLocalDeclarations(function) + LateinitUsageLowering(context).lower(body, function) + + SharedVariablesLowering(context).lower(body, function) + + OuterThisLowering(context).lower(function) + + LocalClassesInInlineLambdasLowering(context).lower(body, function) + + if (!context.config.produce.isCache && functionIsNotFromLazyIr) { + // Do not extract local classes off of inline functions from cached libraries. + LocalClassesInInlineFunctionsLowering(context).lower(body, function) + LocalClassesExtractionFromInlineFunctionsLowering(context).lower(body, function) + } + + WrapInlineDeclarationsWithReifiedTypeParametersLowering(context).lower(body, function) + } + private fun DeclarationTransformer.lowerWithLocalDeclarations(function: IrFunction) { if (transformFlat(function) != null) error("Unexpected transformation of function ${function.dump()}")