From 576e8ba1273726a6182ffe0eeb143fec60668211 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Wed, 28 Dec 2022 15:43:19 +0200 Subject: [PATCH] [K/N] Port to the new driver --- .../kotlin/backend/konan/BitcodeCompiler.kt | 41 ++- .../kotlin/backend/konan/CacheStorage.kt | 53 +++ .../backend/konan/CheckExternalCalls.kt | 4 +- .../kotlin/backend/konan/CompilerOutput.kt | 12 +- .../jetbrains/kotlin/backend/konan/Context.kt | 122 +------ .../backend/konan/DependenciesTracker.kt | 17 +- .../kotlin/backend/konan/FeaturedLibraries.kt | 6 - .../backend/konan/KonanBackendContext.kt | 14 +- .../kotlin/backend/konan/KonanConfig.kt | 4 +- .../backend/konan/KonanLoweringPhases.kt | 128 +------ .../jetbrains/kotlin/backend/konan/Linker.kt | 116 +++--- .../backend/konan/NativeGenerationState.kt | 29 -- .../backend/konan/OptimizationPipeline.kt | 34 +- .../jetbrains/kotlin/backend/konan/PsiToIr.kt | 23 +- .../kotlin/backend/konan/TargetAbiInfo.kt | 13 + .../kotlin/backend/konan/ToplevelPhases.kt | 272 ++------------ .../konan/descriptors/ClassLayoutBuilder.kt | 19 +- .../konan/descriptors/DescriptorUtils.kt | 4 +- .../konan/driver/DynamicCompilerDriver.kt | 25 +- .../kotlin/backend/konan/driver/Machinery.kt | 7 +- .../konan/driver/phases/BackendPhases.kt | 39 +- .../backend/konan/driver/phases/Bitcode.kt | 127 ++++++- .../konan/driver/phases/BitcodeGeneration.kt | 65 ++++ .../konan/driver/phases/CacheBuilding.kt | 47 +++ .../backend/konan/driver/phases/Frontend.kt | 4 - .../kotlin/backend/konan/driver/phases/LTO.kt | 179 ++++++++++ .../backend/konan/driver/phases/Linker.kt | 28 ++ .../backend/konan/driver/phases/Lowerings.kt | 246 +++++++++++++ .../konan/driver/phases/ObjectFiles.kt | 24 ++ .../konan/driver/phases/PhaseBuilders.kt | 34 +- .../backend/konan/driver/phases/PsiToIr.kt | 21 +- .../backend/konan/driver/phases/Serializer.kt | 6 +- .../konan/driver/phases/TopLevelPhases.kt | 178 ++++++--- .../backend/konan/driver/phases/Utilities.kt | 64 ++++ .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 4 +- .../backend/konan/llvm/BitcodePhases.kt | 338 ------------------ .../kotlin/backend/konan/llvm/IrToBitcode.kt | 33 +- .../backend/konan/llvm/LlvmDeclarations.kt | 2 +- .../backend/konan/llvm/RTTIGenerator.kt | 13 +- .../konan/llvm/coverage/CoverageManager.kt | 9 +- .../objcexport/ObjCExportCodeGenerator.kt | 3 +- .../konan/lower/BuiltinOperatorLowering.kt | 2 +- .../backend/konan/lower/CacheInfoBuilder.kt | 2 +- .../konan/lower/DataClassOperatorsLowering.kt | 2 +- .../backend/konan/lower/TestProcessor.kt | 2 +- .../konan/optimizations/CallGraphBuilder.kt | 4 +- .../kotlin/backend/konan/optimizations/DCE.kt | 110 ++++++ .../backend/konan/optimizations/DFGBuilder.kt | 8 +- .../backend/konan/optimizations/DataFlowIR.kt | 20 +- .../optimizations/DevirtualizationAnalysis.kt | 49 ++- .../konan/optimizations/EscapeAnalysis.kt | 17 +- .../StaticInitializersOptimization.kt | 8 +- .../konan/serialization/KonanIrlinker.kt | 8 +- 53 files changed, 1474 insertions(+), 1165 deletions(-) create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheStorage.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BitcodeGeneration.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CacheBuilding.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/LTO.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Linker.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/ObjectFiles.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Utilities.kt delete mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt create mode 100644 kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DCE.kt diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeCompiler.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeCompiler.kt index 238290d5b2c..6e6a1f2a320 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeCompiler.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/BitcodeCompiler.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.backend.konan +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.target.* @@ -12,11 +14,14 @@ typealias BitcodeFile = String typealias ObjectFile = String typealias ExecutableFile = String -internal class BitcodeCompiler(val generationState: NativeGenerationState) { +internal class BitcodeCompiler( + private val context: PhaseContext, + private val temporaryFiles: TempFiles, +) { - private val config = generationState.config + private val config = context.config private val platform = config.platform - private val optimize = generationState.shouldOptimize() + private val optimize = context.shouldOptimize() private val debug = config.debug private val overrideClangOptions = @@ -28,11 +33,11 @@ internal class BitcodeCompiler(val generationState: NativeGenerationState) { private fun runTool(vararg command: String) = Command(*command) - .logWith(generationState::log) + .logWith(context::log) .execute() private fun temporary(name: String, suffix: String): String = - generationState.tempFiles.create(name, suffix).absolutePath + temporaryFiles.create(name, suffix).absolutePath private fun targetTool(tool: String, vararg arg: String) { val absoluteToolName = if (platform.configurables is AppleConfigurables) { @@ -58,19 +63,19 @@ internal class BitcodeCompiler(val generationState: NativeGenerationState) { } val flags = overrideClangOptions.takeIf(List::isNotEmpty) ?: mutableListOf().apply { - addNonEmpty(configurables.clangFlags) - addNonEmpty(listOf("-triple", targetTriple.toString())) - if (configurables is ZephyrConfigurables) { - addNonEmpty(configurables.constructClangCC1Args()) - } - addNonEmpty(when { - optimize -> configurables.clangOptFlags - debug -> configurables.clangDebugFlags - else -> configurables.clangNooptFlags - }) - addNonEmpty(BitcodeEmbedding.getClangOptions(config)) - addNonEmpty(configurables.currentRelocationMode(generationState).translateToClangCc1Flag()) - } + addNonEmpty(configurables.clangFlags) + addNonEmpty(listOf("-triple", targetTriple.toString())) + if (configurables is ZephyrConfigurables) { + addNonEmpty(configurables.constructClangCC1Args()) + } + addNonEmpty(when { + optimize -> configurables.clangOptFlags + debug -> configurables.clangDebugFlags + else -> configurables.clangNooptFlags + }) + addNonEmpty(BitcodeEmbedding.getClangOptions(config)) + addNonEmpty(configurables.currentRelocationMode(context).translateToClangCc1Flag()) + } if (configurables is AppleConfigurables) { targetTool("clang++", *flags.toTypedArray(), file, "-o", objectFile) } else { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheStorage.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheStorage.kt new file mode 100644 index 00000000000..51119e34570 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheStorage.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan + +import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer +import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer +import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer + +internal class CacheStorage(private val generationState: NativeGenerationState) { + private val outputFiles = generationState.outputFiles + + companion object { + fun renameOutput(outputFiles: OutputFiles) { + // For caches the output file is a directory. It might be created by someone else, + // we have to delete it in order for the next renaming operation to succeed. + // TODO: what if the directory is not empty? + java.io.File(outputFiles.mainFileName).delete() + if (!outputFiles.tempCacheDirectory!!.renameTo(outputFiles.mainFile)) + outputFiles.tempCacheDirectory.deleteRecursively() + } + } + + fun saveAdditionalCacheInfo() { + outputFiles.prepareTempDirectories() + saveCacheBitcodeDependencies() + saveInlineFunctionBodies() + saveClassFields() + saveEagerInitializedProperties() + } + + private fun saveCacheBitcodeDependencies() { + outputFiles.bitcodeDependenciesFile!!.writeLines( + DependenciesSerializer.serialize(generationState.dependenciesTracker.immediateBitcodeDependencies)) + } + + private fun saveInlineFunctionBodies() { + outputFiles.inlineFunctionBodiesFile!!.writeBytes( + InlineFunctionBodyReferenceSerializer.serialize(generationState.inlineFunctionBodies)) + } + + private fun saveClassFields() { + outputFiles.classFieldsFile!!.writeBytes( + ClassFieldsSerializer.serialize(generationState.classFields)) + } + + private fun saveEagerInitializedProperties() { + outputFiles.eagerInitializedPropertiesFile!!.writeBytes( + EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles)) + } +} diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt index 5f84e99566b..c5159328b52 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CheckExternalCalls.kt @@ -182,7 +182,7 @@ internal fun checkLlvmModuleExternalCalls(generationState: NativeGenerationState // otherwise optimiser can inline it staticData.getGlobal(functionListGlobal)?.setExternallyInitialized(true); staticData.getGlobal(functionListSizeGlobal)?.setExternallyInitialized(true); - generationState.verifyBitCode() + verifyModule(llvm.module) } // this should be a separate pass, to handle DCE correctly @@ -201,5 +201,5 @@ internal fun addFunctionsListSymbolForChecker(generationState: NativeGenerationS staticData.getGlobal(functionListSizeGlobal) ?.setInitializer(llvm.constInt32(functions.size)) ?: throw IllegalStateException("$functionListSizeGlobal global not found") - generationState.verifyBitCode() + verifyModule(llvm.module) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index df68fc1edae..bb6f72bddd7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -19,6 +19,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.createObjCFramework import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.konan.CURRENT import org.jetbrains.kotlin.konan.CompilerVersion +import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.file.isBitcode import org.jetbrains.kotlin.konan.library.KONAN_STDLIB_NAME import org.jetbrains.kotlin.konan.library.impl.buildLibrary @@ -78,15 +79,14 @@ val CompilerOutputKind.isCache: Boolean val KonanConfig.involvesCodegen: Boolean get() = produce != CompilerOutputKind.LIBRARY && !omitFrameworkBinary -internal fun llvmIrDumpCallback(state: ActionState, module: IrModuleFragment, context: Context) { - module.let{} - if (state.beforeOrAfter == BeforeOrAfter.AFTER && state.phase.name in context.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) { +internal fun llvmIrDumpCallback(state: ActionState, module: LLVMModuleRef, config: KonanConfig, temporaryFiles: TempFiles) { + if (state.phase.name in config.configuration.getList(KonanConfigKeys.SAVE_LLVM_IR)) { val moduleName: String = memScoped { val sizeVar = alloc() - LLVMGetModuleIdentifier(context.generationState.llvm.module, sizeVar.ptr)!!.toKStringFromUtf8() + LLVMGetModuleIdentifier(module, sizeVar.ptr)!!.toKStringFromUtf8() } - val output = context.generationState.tempFiles.create("$moduleName.${state.phase.name}", ".ll") - if (LLVMPrintModuleToFile(context.generationState.llvm.module, output.absolutePath, null) != 0) { + val output = temporaryFiles.create("$moduleName.${state.phase.name}", ".ll") + if (LLVMPrintModuleToFile(module, output.absolutePath, null) != 0) { error("Can't dump LLVM IR to ${output.absolutePath}") } } 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 f56e080b539..17980289499 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 @@ -16,8 +16,10 @@ import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysisRes 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 @@ -29,6 +31,7 @@ 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 @@ -68,35 +71,18 @@ internal class NativeMapping : DefaultMapping() { val functionToVolatileField = DefaultDelegateFactory.newDeclarationToDeclarationMapping() } +// TODO: Can be renamed or merged with KonanBackendContext internal class Context( config: KonanConfig, - val environment: KotlinCoreEnvironment, - override var bindingContext: BindingContext, - val moduleDescriptor: ModuleDescriptor, -) : KonanBackendContext(config), PsiToIrContext { + val sourcesModules: Set, + override val builtIns: KonanBuiltIns, + override val irBuiltIns: IrBuiltIns, + val irModules: Map, + val irLinker: KonanIrLinker, + symbols: KonanSymbols, +) : KonanBackendContext(config) { - fun populateAfterPsiToIr( - psiToIrOutput: PsiToIrOutput - ) { - irModules = psiToIrOutput.irModules - irModule = psiToIrOutput.irModule - expectDescriptorToSymbol = psiToIrOutput.expectDescriptorToSymbol - ir = KonanIr(this) - ir.symbols = psiToIrOutput.symbols - if (psiToIrOutput.irLinker is KonanIrLinker) { - irLinker = psiToIrOutput.irLinker - } - } - - override var symbolTable: SymbolTable? = null - - lateinit var cAdapterExportedElements: CAdapterExportedElements - - lateinit var expectDescriptorToSymbol: MutableMap - - override val builtIns: KonanBuiltIns by lazy(PUBLICATION) { - moduleDescriptor.builtIns as KonanBuiltIns - } + override val ir: KonanIr = KonanIr(this, symbols) override val configuration get() = config.configuration @@ -104,20 +90,12 @@ internal class Context( override val optimizeLoopsOverUnsignedArrays = true - // TODO: Drop it to reduce code coupling and make it possible to have multiple - // generationStates at the same time. - lateinit var generationState: NativeGenerationState - val innerClassesSupport by lazy { InnerClassesSupport(mapping, irFactory) } val bridgesSupport by lazy { BridgesSupport(mapping, irBuiltIns, irFactory) } val inlineFunctionsSupport by lazy { InlineFunctionsSupport(mapping) } val enumsSupport by lazy { EnumsSupport(mapping, irBuiltIns, irFactory) } val cachesAbiSupport by lazy { CachesAbiSupport(mapping, irFactory) } - override val reflectionTypes: KonanReflectionTypes by lazy(PUBLICATION) { - KonanReflectionTypes(moduleDescriptor) - } - // TODO: Remove after adding special property to IrDeclaration. private val layoutBuilders = mutableMapOf() @@ -136,93 +114,27 @@ internal class Context( lateinit var globalHierarchyAnalysisResult: GlobalHierarchyAnalysisResult - val librariesWithDependencies by lazy { - config.librariesWithDependencies(moduleDescriptor) - } - - fun needGlobalInit(field: IrField): Boolean { - if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return field.isStatic - // TODO: add some smartness here. Maybe if package of the field is in never accessed - // assume its global init can be actually omitted. - return true - } - - lateinit var irModules: Map - - // TODO: make lateinit? - var irModule: IrModuleFragment? = null - set(module) { - if (field != null) { - throw Error("Another IrModule in the context.") - } - field = module!! - ir = KonanIr(this) - } - - override lateinit var ir: KonanIr - - override val irBuiltIns - get() = irModule!!.irBuiltins - override val typeSystem: IrTypeSystemContext get() = IrTypeSystemContextImpl(irBuiltIns) - override val interopBuiltIns by lazy { + val interopBuiltIns by lazy { InteropBuiltIns(this.builtIns) } + var cAdapterExportedElements: CAdapterExportedElements? = null var objCExportedInterface: ObjCExportedInterface? = null var objCExportCodeSpec: ObjCExportCodeSpec? = null - lateinit var library: KonanLibraryLayout - - fun verifyBitCode() { - if (::generationState.isInitialized) - generationState.verifyBitCode() - } - - fun printBitCode() { - if (::generationState.isInitialized) - generationState.printBitCode() - } - fun ghaEnabled() = ::globalHierarchyAnalysisResult.isInitialized - val memoryModel = config.memoryModel - - override var inVerbosePhase = false - override fun log(message: () -> String) { - if (inVerbosePhase) { - println(message()) - } - } - - var moduleDFG: ModuleDFG? = null - val lifetimes = mutableMapOf() - var devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult? = null - - var referencedFunctions: Set? = null - - override val stdlibModule + val stdlibModule get() = this.builtIns.any.module val declaredLocalArrays: MutableMap = HashMap() - lateinit var irLinker: KonanIrLinker + val targetAbiInfo = config.target.abiInfo - val targetAbiInfo: TargetAbiInfo by lazy { - when { - config.target == KonanTarget.MINGW_X64 -> { - WindowsX64TargetAbiInfo() - } - !config.target.family.isAppleFamily && config.target.architecture == Architecture.ARM64 -> { - AAPCS64TargetAbiInfo() - } - else -> { - DefaultTargetAbiInfo() - } - } - } + val memoryModel = config.memoryModel override fun dispose() {} } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt index 243b7ed41fd..6bc0ad37ad8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/DependenciesTracker.kt @@ -76,7 +76,7 @@ internal class DependenciesTrackerImpl(private val generationState: NativeGenera private val usedNativeDependencies = mutableSetOf() private val usedBitcodeOfFile = mutableSetOf() - private val allLibraries by lazy { context.librariesWithDependencies.toSet() } + private val allLibraries by lazy { context.config.librariesWithDependencies().toSet() } private fun findStdlibFile(fqName: FqName, fileName: String): LibraryFile { val stdlib = (context.standardLlvmSymbolsOrigin as? DeserializedKlibModuleOrigin)?.library @@ -276,7 +276,7 @@ internal class DependenciesTrackerImpl(private val generationState: NativeGenera val allBitcodeDependencies: List = run { val allBitcodeDependencies = mutableMapOf() - for (library in context.librariesWithDependencies) { + for (library in context.config.librariesWithDependencies()) { if (context.config.cachedLibraries.getLibraryCache(library) == null || library == context.config.libraryToCache?.klib) allBitcodeDependencies[library] = DependenciesTracker.ResolvedDependency.wholeModule(library) } @@ -352,4 +352,15 @@ internal object DependenciesSerializer { } private const val DEPENDENCIES_DELIMITER = '|' -} \ No newline at end of file +} + +/** + * Result of dependency tracking during LLVM module production. + * Elements of this class should be easily serializable/deserializable, + * so the late compiler phases could be easily executed separately. + */ +data class DependenciesTrackingResult( + val nativeDependenciesToLink: List, + val allNativeDependencies: List, + val allCachedBitcodeDependencies: List +) \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FeaturedLibraries.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FeaturedLibraries.kt index eb6aa3f1a2d..c37abd8575a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FeaturedLibraries.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/FeaturedLibraries.kt @@ -19,15 +19,9 @@ import org.jetbrains.kotlin.library.metadata.klibModuleOrigin import org.jetbrains.kotlin.library.metadata.resolver.KotlinLibraryResolveResult import org.jetbrains.kotlin.library.toUnresolvedLibraries -internal fun Context.getExportedDependencies(): List = - moduleDescriptor.getExportedDependencies(config) - internal fun ModuleDescriptor.getExportedDependencies(konanConfig: KonanConfig): List = getDescriptorsFromLibraries((konanConfig.resolve.exportedLibraries + konanConfig.resolve.includedLibraries).toSet()) -internal fun Context.getIncludedLibraryDescriptors(): List = - moduleDescriptor.getIncludedLibraryDescriptors(config) - internal fun ModuleDescriptor.getIncludedLibraryDescriptors(konanConfig: KonanConfig): List = getDescriptorsFromLibraries(konanConfig.resolve.includedLibraries.toSet()) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt index 87ba25b03a7..95e6278bde3 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanBackendContext.kt @@ -9,6 +9,7 @@ import org.jetbrains.kotlin.backend.common.CommonBackendContext import org.jetbrains.kotlin.backend.common.DefaultMapping import org.jetbrains.kotlin.backend.common.Mapping import org.jetbrains.kotlin.backend.konan.descriptors.KonanSharedVariablesManager +import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext import org.jetbrains.kotlin.backend.konan.ir.KonanIr import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys @@ -23,7 +24,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl import org.jetbrains.kotlin.ir.util.getPackageFragment -internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBackendContext { +internal abstract class KonanBackendContext(config: KonanConfig) : BasicPhaseContext(config), CommonBackendContext { abstract override val builtIns: KonanBuiltIns abstract override val ir: KonanIr @@ -36,17 +37,6 @@ internal abstract class KonanBackendContext(val config: KonanConfig) : CommonBac KonanSharedVariablesManager(this) } - val messageCollector: MessageCollector - get() = config.configuration.getNotNull(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY) - - override fun report(element: IrElement?, irFile: IrFile?, message: String, isError: Boolean) { - val location = element?.getCompilerMessageLocation(irFile ?: error("irFile should be not null for $element")) - this.messageCollector.report( - if (isError) CompilerMessageSeverity.ERROR else CompilerMessageSeverity.WARNING, - message, location - ) - } - override val internalPackageFqn = KonanFqNames.internalPackageName override val mapping: NativeMapping = NativeMapping() 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 e49a2561f78..e46e8981b17 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 @@ -239,9 +239,7 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration val shortModuleName: String? get() = configuration.get(KonanConfigKeys.SHORT_MODULE_NAME) - fun librariesWithDependencies(moduleDescriptor: ModuleDescriptor?): List { - - if (moduleDescriptor == null) error("purgeUnneeded() only works correctly after resolve is over, and we have successfully marked package files as needed or not needed.") + fun librariesWithDependencies(): List { return resolvedLibraries.filterRoots { (!it.isDefault && !this.purgeUserLibs) || it.isNeededForLink }.getFullList(TopologicalLibraryOrder).map { it as KonanLibrary } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt index eaeafae87db..b7a17e854cf 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanLoweringPhases.kt @@ -1,26 +1,21 @@ package org.jetbrains.kotlin.backend.konan -import org.jetbrains.kotlin.backend.common.* +import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.* -import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering -import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesExtractionFromInlineFunctionsLowering import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineFunctionsLowering import org.jetbrains.kotlin.backend.common.lower.inline.LocalClassesInInlineLambdasLowering import org.jetbrains.kotlin.backend.common.lower.loops.ForLoopsLowering import org.jetbrains.kotlin.backend.common.lower.optimizations.FoldConstantLowering -import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering -import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering import org.jetbrains.kotlin.backend.common.phaser.* +import org.jetbrains.kotlin.backend.common.runOnFilePostfix import org.jetbrains.kotlin.backend.konan.ir.FunctionsWithoutBoundCheckGenerator -import org.jetbrains.kotlin.backend.konan.llvm.redundantCoercionsCleaningPhase import org.jetbrains.kotlin.backend.konan.lower.* import org.jetbrains.kotlin.backend.konan.lower.InitializersLowering import org.jetbrains.kotlin.backend.konan.optimizations.KonanBCEForLoopBodyTransformer import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrFunction -import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid @@ -32,7 +27,7 @@ private val filePhaseActions = setOfNotNull( ) private val modulePhaseActions = setOfNotNull( defaultDumper, - ::llvmIrDumpCallback, +// ::llvmIrDumpCallback, ::moduleValidationCallback.takeIf { validateAll } ) @@ -66,42 +61,8 @@ internal fun makeKonanFileOpPhase( actions = filePhaseActions ) -internal fun makeKonanModuleOpPhase( - op: (Context, IrModuleFragment) -> Unit, - name: String, - description: String, - prerequisite: Set> = emptySet() -) = SameTypeNamedCompilerPhase( - name, description, prerequisite, nlevels = 0, - lower = object : SameTypeCompilerPhase { - override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: IrModuleFragment): IrModuleFragment { - op(context, input) - return input - } - }, - actions = modulePhaseActions -) - -internal val specialBackendChecksPhase = makeKonanModuleLoweringPhase( - { SpecialBackendChecksTraversal(it, it.interopBuiltIns, it.ir.symbols, it.irBuiltIns) }, - name = "SpecialBackendChecks", - description = "Special backend checks" -) - -internal val propertyAccessorInlinePhase = makeKonanModuleLoweringPhase( - ::PropertyAccessorInlineLowering, - name = "PropertyAccessorInline", - description = "Property accessor inline lowering" -) - /* IrFile phases */ -internal val createFileLowerStatePhase = makeKonanFileOpPhase( - { context, _ -> context.generationState.fileLowerState = FileLowerState() }, - name = "CreateFileLowerState", - description = "Create FileLowerState" -) - internal val removeExpectDeclarationsPhase = makeKonanFileLoweringPhase( ::ExpectDeclarationsRemoving, name = "RemoveExpectDeclarations", @@ -149,12 +110,6 @@ internal val sharedVariablesPhase = makeKonanFileLoweringPhase( prerequisite = setOf(lateinitPhase) ) -internal val inventNamesForLocalClasses = makeKonanFileLoweringPhase( - { NativeInventNamesForLocalClasses(it.generationState) }, - name = "InventNamesForLocalClasses", - description = "Invent names for local classes and anonymous objects" -) - internal val extractLocalClassesFromInlineBodies = makeKonanFileOpPhase( { context, irFile -> irFile.acceptChildrenVoid(object : IrElementVisitorVoid { @@ -184,15 +139,6 @@ internal val wrapInlineDeclarationsWithReifiedTypeParametersLowering = makeKonan description = "Wrap inline declarations with reified type parameters" ) -internal val inlinePhase = makeKonanFileOpPhase( - { context, irFile -> - FunctionInlining(context, NativeInlineFunctionResolver(context, context.generationState)).lower(irFile) - }, - name = "Inline", - description = "Functions inlining", - prerequisite = setOf(lowerBeforeInlinePhase, arrayConstructorPhase, extractLocalClassesFromInlineBodies) -) - internal val postInlinePhase = makeKonanFileLoweringPhase( { PostInlineLowering(it) }, name = "PostInline", @@ -250,12 +196,6 @@ internal val initializersPhase = makeKonanFileLoweringPhase( prerequisite = setOf(enumConstructorsPhase) ) -internal val objectClassesPhase = makeKonanFileOpPhase( - op = { context, irFile -> ObjectClassLowering(context.generationState).lower(irFile) }, - name = "ObjectClasses", - description = "Object classes lowering" -) - internal val localFunctionsPhase = makeKonanFileOpPhase( op = { context, irFile -> LocalDelegatedPropertiesLowering().lower(irFile) @@ -339,39 +279,25 @@ internal val testProcessorPhase = makeKonanFileOpPhase( description = "Unit test processor" ) -internal val delegationPhase = makeKonanFileLoweringPhase( - { PropertyDelegationLowering(it.generationState) }, - name = "Delegation", - description = "Delegation lowering", - prerequisite = setOf(volatilePhase) -) - -internal val functionReferencePhase = makeKonanFileLoweringPhase( - { FunctionReferenceLowering(it.generationState) }, - name = "FunctionReference", - description = "Function references lowering", - prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase` -) - internal val enumWhenPhase = makeKonanFileLoweringPhase( ::NativeEnumWhenLowering, name = "EnumWhen", description = "Enum when lowering", - prerequisite = setOf(enumConstructorsPhase, functionReferencePhase) +// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase) ) internal val enumClassPhase = makeKonanFileLoweringPhase( ::EnumClassLowering, name = "Enums", description = "Enum classes lowering", - prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumWhenPhase) // TODO: make weak dependency on `testProcessorPhase` +// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumWhenPhase) // TODO: make weak dependency on `testProcessorPhase` ) internal val enumUsagePhase = makeKonanFileLoweringPhase( ::EnumUsageLowering, name = "EnumUsage", description = "Enum usage lowering", - prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumClassPhase) +// prerequisite = setOf(enumConstructorsPhase, functionReferencePhase, enumClassPhase) ) @@ -379,7 +305,7 @@ internal val singleAbstractMethodPhase = makeKonanFileLoweringPhase( ::NativeSingleAbstractMethodLowering, name = "SingleAbstractMethod", description = "Replace SAM conversions with instances of interface-implementing classes", - prerequisite = setOf(functionReferencePhase) +// prerequisite = setOf(functionReferencePhase) ) internal val builtinOperatorPhase = makeKonanFileLoweringPhase( @@ -389,37 +315,18 @@ internal val builtinOperatorPhase = makeKonanFileLoweringPhase( prerequisite = setOf(defaultParameterExtentPhase, singleAbstractMethodPhase, enumWhenPhase) ) -internal val interopPhase = makeKonanFileLoweringPhase( - { InteropLowering(it.generationState) }, - name = "Interop", - description = "Interop lowering", - prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase) -) - internal val varargPhase = makeKonanFileLoweringPhase( ::VarargInjectionLowering, name = "Vararg", description = "Vararg lowering", - prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck) -) - -internal val coroutinesPhase = makeKonanFileOpPhase( - { context, irFile -> - NativeSuspendFunctionsLowering(context.generationState).lower(irFile) - AddContinuationToNonLocalSuspendFunctionsLowering(context).lower(irFile) - NativeAddContinuationToFunctionCallsLowering(context).lower(irFile) - AddFunctionSupertypeToSuspendFunctionLowering(context).lower(irFile) - }, - name = "Coroutines", - description = "Coroutines lowering", - prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase) +// prerequisite = setOf(functionReferencePhase, defaultParameterExtentPhase, interopPhase, functionsWithoutBoundCheck) ) internal val typeOperatorPhase = makeKonanFileLoweringPhase( ::TypeOperatorLowering, name = "TypeOperators", description = "Type operators lowering", - prerequisite = setOf(coroutinesPhase) +// prerequisite = setOf(coroutinesPhase) ) internal val bridgesPhase = makeKonanFileOpPhase( @@ -429,21 +336,14 @@ internal val bridgesPhase = makeKonanFileOpPhase( }, name = "Bridges", description = "Bridges building", - prerequisite = setOf(coroutinesPhase) +// prerequisite = setOf(coroutinesPhase) ) internal val autoboxPhase = makeKonanFileLoweringPhase( ::Autoboxing, name = "Autobox", description = "Autoboxing of primitive types", - prerequisite = setOf(bridgesPhase, coroutinesPhase) -) - -internal val unboxInlinePhase = makeKonanModuleLoweringPhase( - ::UnboxInlineLowering, - name = "UnboxInline", - description = "Unbox functions inline lowering", - prerequisite = setOf(autoboxPhase, redundantCoercionsCleaningPhase) +// prerequisite = setOf(bridgesPhase, coroutinesPhase) ) internal val expressionBodyTransformPhase = makeKonanFileLoweringPhase( @@ -488,10 +388,4 @@ internal val exportInternalAbiPhase = makeKonanFileLoweringPhase( ::ExportCachesAbiVisitor, name = "ExportInternalAbi", description = "Add accessors to private entities" -) - -internal val useInternalAbiPhase = makeKonanFileLoweringPhase( - { ImportCachesAbiTransformer(it.generationState) }, - name = "UseInternalAbi", - description = "Use internal ABI functions to access private entities" ) \ No newline at end of file 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 845f8a8caca..69c717401a8 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 @@ -5,6 +5,7 @@ import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer import org.jetbrains.kotlin.konan.KonanExternalToolFailure +import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.library.KonanLibrary @@ -36,68 +37,36 @@ internal fun determineLinkerOutput(context: PhaseContext): LinkerOutputKind = else -> TODO("${context.config.produce} should not reach native linker stage") } -internal class CacheStorage(val generationState: NativeGenerationState) { - private val config = generationState.config - private val outputFiles = generationState.outputFiles - - fun renameOutput() { - // For caches the output file is a directory. It might be created by someone else, - // we have to delete it in order for the next renaming operation to succeed. - // TODO: what if the directory is not empty? - java.io.File(outputFiles.mainFileName).delete() - if (!outputFiles.tempCacheDirectory!!.renameTo(outputFiles.mainFile)) - outputFiles.tempCacheDirectory.deleteRecursively() - } - - fun saveAdditionalCacheInfo() { - outputFiles.prepareTempDirectories() - saveCacheBitcodeDependencies() - saveInlineFunctionBodies() - saveClassFields() - saveEagerInitializedProperties() - } - - private fun saveCacheBitcodeDependencies() { - outputFiles.bitcodeDependenciesFile!!.writeLines( - DependenciesSerializer.serialize(generationState.dependenciesTracker.immediateBitcodeDependencies)) - } - - private fun saveInlineFunctionBodies() { - outputFiles.inlineFunctionBodiesFile!!.writeBytes( - InlineFunctionBodyReferenceSerializer.serialize(generationState.inlineFunctionBodies)) - } - - private fun saveClassFields() { - outputFiles.classFieldsFile!!.writeBytes( - ClassFieldsSerializer.serialize(generationState.classFields)) - } - - private fun saveEagerInitializedProperties() { - outputFiles.eagerInitializedPropertiesFile!!.writeBytes( - EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles)) - } -} - // TODO: We have a Linker.kt file in the shared module. -internal class Linker(val generationState: NativeGenerationState) { - private val config = generationState.config +internal class Linker( + private val context: PhaseContext, + private val isCoverageEnabled: Boolean = false, + private val tempFiles: TempFiles, + private val outputFiles: OutputFiles, +) { + + private val config = context.config private val platform = config.platform - private val linkerOutput = determineLinkerOutput(generationState) + private val linkerOutput = determineLinkerOutput(context) private val linker = platform.linker private val target = config.target - private val optimize = generationState.shouldOptimize() + private val optimize = context.shouldOptimize() private val debug = config.debug || config.lightDebug - fun link(objectFiles: List) { - val nativeDependencies = generationState.dependenciesTracker.nativeDependenciesToLink + fun link( + outputFile: String, + objectFiles: List, + dependenciesTrackingResult: DependenciesTrackingResult + ) { + val nativeDependencies = dependenciesTrackingResult.nativeDependenciesToLink val includedBinariesLibraries = config.libraryToCache?.let { listOf(it.klib) } ?: nativeDependencies.filterNot { config.cachedLibraries.isLibraryCached(it) } val includedBinaries = includedBinariesLibraries.map { (it as? KonanLibrary)?.includedPaths.orEmpty() }.flatten() - val libraryProvidedLinkerFlags = generationState.dependenciesTracker.allNativeDependencies.map { it.linkerOpts }.flatten() + val libraryProvidedLinkerFlags = dependenciesTrackingResult.allNativeDependencies.map { it.linkerOpts }.flatten() - runLinker(objectFiles, includedBinaries, libraryProvidedLinkerFlags) + runLinker(outputFile, objectFiles, includedBinaries, libraryProvidedLinkerFlags, dependenciesTrackingResult) } private fun asLinkerArgs(args: List): List { @@ -117,11 +86,13 @@ internal class Linker(val generationState: NativeGenerationState) { return result } - private fun runLinker(objectFiles: List, - includedBinaries: List, - libraryProvidedLinkerFlags: List): ExecutableFile? { - val outputFiles = generationState.outputFiles - + private fun runLinker( + outputFile: String, + objectFiles: List, + includedBinaries: List, + libraryProvidedLinkerFlags: List, + dependenciesTrackingResult: DependenciesTrackingResult, + ): ExecutableFile { val additionalLinkerArgs: List val executable: String @@ -137,7 +108,7 @@ internal class Linker(val generationState: NativeGenerationState) { } executable = outputFiles.nativeBinaryFile } else { - val framework = File(generationState.outputFile) + val framework = File(outputFile) val dylibName = framework.name.removeSuffix(".framework") val dylibRelativePath = when (target.family) { Family.IOS, @@ -152,10 +123,9 @@ internal class Linker(val generationState: NativeGenerationState) { executable = dylibPath.absolutePath } - val needsProfileLibrary = generationState.coverage.enabled val mimallocEnabled = config.allocationMode == AllocationMode.MIMALLOC - val linkerInput = determineLinkerInput(objectFiles, linkerOutput) + val linkerInput = determineLinkerInput(objectFiles, linkerOutput, dependenciesTrackingResult) try { File(executable).delete() val linkerArgs = asLinkerArgs(config.configuration.getNotNull(KonanConfigKeys.LINKER_ARGS)) + @@ -172,12 +142,12 @@ internal class Linker(val generationState: NativeGenerationState) { debug = debug, kind = linkerOutput, outputDsymBundle = outputFiles.symbolicInfoFile, - needsProfileLibrary = needsProfileLibrary, + needsProfileLibrary = isCoverageEnabled, mimallocEnabled = mimallocEnabled, sanitizer = config.sanitizer ) (linkerInput.preLinkCommands + finalOutputCommands).forEach { - it.logWith(generationState::log) + it.logWith(context::log) it.execute() } } catch (e: KonanExternalToolFailure) { @@ -191,7 +161,7 @@ internal class Linker(val generationState: NativeGenerationState) { Also, consider filing an issue with full Gradle log here: https://kotl.in/issue """.trimIndent() else "" - generationState.reportCompilationError("${e.toolName} invocation reported errors\n$extraUserInfo\n${e.message}") + context.reportCompilationError("${e.toolName} invocation reported errors\n$extraUserInfo\n${e.message}") } return executable } @@ -205,8 +175,12 @@ internal class Linker(val generationState: NativeGenerationState) { return isStaticLibrary && enabled && nonEmptyCaches } - private fun determineLinkerInput(objectFiles: List, linkerOutputKind: LinkerOutputKind): LinkerInput { - val caches = determineCachesToLink(generationState) + private fun determineLinkerInput( + objectFiles: List, + linkerOutputKind: LinkerOutputKind, + dependenciesTrackingResult: DependenciesTrackingResult, + ): LinkerInput { + val caches = determineCachesToLink(context, dependenciesTrackingResult) // Since we have several linker stages that involve caching, // we should detect cache usage early to report errors correctly. val cachingInvolved = caches.static.isNotEmpty() || caches.dynamic.isNotEmpty() @@ -216,7 +190,7 @@ internal class Linker(val generationState: NativeGenerationState) { LinkerInput(objectFiles, CachesToLink(emptyList(), caches.dynamic), emptyList(), cachingInvolved) } shouldPerformPreLink(caches, linkerOutputKind) -> { - val preLinkResult = generationState.tempFiles.create("withStaticCaches", ".o").absolutePath + val preLinkResult = tempFiles.create("withStaticCaches", ".o").absolutePath val preLinkCommands = linker.preLinkCommands(objectFiles + caches.static, preLinkResult) LinkerInput(listOf(preLinkResult), CachesToLink(emptyList(), caches.dynamic), preLinkCommands, cachingInvolved) } @@ -234,20 +208,18 @@ private class LinkerInput( private class CachesToLink(val static: List, val dynamic: List) -private fun determineCachesToLink(generationState: NativeGenerationState): CachesToLink { +private fun determineCachesToLink( + context: PhaseContext, + dependenciesTrackingResult: DependenciesTrackingResult, +): CachesToLink { val staticCaches = mutableListOf() val dynamicCaches = mutableListOf() - generationState.dependenciesTracker.allCachedBitcodeDependencies.forEach { dependency -> + dependenciesTrackingResult.allCachedBitcodeDependencies.forEach { dependency -> val library = dependency.library - val currentBinaryContainsLibrary = generationState.llvmModuleSpecification.containsLibrary(library) - val cache = generationState.config.cachedLibraries.getLibraryCache(library) + val cache = context.config.cachedLibraries.getLibraryCache(library) ?: error("Library $library is expected to be cached") - // Consistency check. Generally guaranteed by implementation. - if (currentBinaryContainsLibrary) - error("Library ${library.libraryName} is found in both cache and current binary") - val list = when (cache.kind) { CachedLibraries.Kind.DYNAMIC -> dynamicCaches CachedLibraries.Kind.STATIC -> staticCaches 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 09ae5f83261..8dbd436e836 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 @@ -97,43 +97,14 @@ internal class NativeGenerationState( val cStubsManager = CStubsManager(config.target, this) lateinit var llvmDeclarations: LlvmDeclarations - lateinit var bitcodeFileName: String - - lateinit var compilerOutput: List - val coverage by lazy { CoverageManager(this) } lateinit var objCExport: ObjCExport fun hasDebugInfo() = debugInfoDelegate.isInitialized() - fun verifyBitCode() { - if (!llvmDelegate.isInitialized()) return - verifyModule(llvm.module) - } - - // TODO: Do we need this function? - fun printBitCode() { - if (!llvmDelegate.isInitialized()) return - separator("BitCode:") - LLVMDumpModule(llvm.module) - } - - private fun separator(title: String) { - println("\n\n--- ${title} ----------------------\n") - } - private var isDisposed = false - // A little hack to make logging work when executing this phase in its parent context. - // TODO: A better solution would be a separate logger object or something like that. - // PhaseContext should not be responsible for logging. - override var inVerbosePhase: Boolean - get() = context.inVerbosePhase - set(value) { - context.inVerbosePhase = value - } - override fun dispose() { if (isDisposed) return diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt index cd3059739da..1197553d151 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/OptimizationPipeline.kt @@ -116,36 +116,40 @@ internal fun createLTOPipelineConfigForRuntime(generationState: NativeGeneration * In case of debug we do almost nothing (that's why we need [createLTOPipelineConfigForRuntime]), * but for release binaries we rely on "closed" world and enable a lot of optimizations. */ -internal fun createLTOFinalPipelineConfig(generationState: NativeGenerationState): LlvmPipelineConfig { - val config = generationState.config +internal fun createLTOFinalPipelineConfig( + context: PhaseContext, + targetTriple: String, + closedWorld: Boolean +): LlvmPipelineConfig { + val config = context.config val target = config.target val configurables: Configurables = config.platform.configurables - val cpuModel = getCpuModel(generationState) - val cpuFeatures = getCpuFeatures(generationState) + val cpuModel = getCpuModel(context) + val cpuFeatures = getCpuFeatures(context) val optimizationLevel: LlvmOptimizationLevel = when { - generationState.shouldOptimize() -> LlvmOptimizationLevel.AGGRESSIVE - generationState.shouldContainDebugInfo() -> LlvmOptimizationLevel.NONE + context.shouldOptimize() -> LlvmOptimizationLevel.AGGRESSIVE + context.shouldContainDebugInfo() -> LlvmOptimizationLevel.NONE else -> LlvmOptimizationLevel.DEFAULT } val sizeLevel: LlvmSizeLevel = when { // We try to optimize code as much as possible on embedded targets. target is KonanTarget.ZEPHYR || target == KonanTarget.WASM32 -> LlvmSizeLevel.AGGRESSIVE - generationState.shouldOptimize() -> LlvmSizeLevel.NONE - generationState.shouldContainDebugInfo() -> LlvmSizeLevel.NONE + context.shouldOptimize() -> LlvmSizeLevel.NONE + context.shouldContainDebugInfo() -> LlvmSizeLevel.NONE else -> LlvmSizeLevel.NONE } val codegenOptimizationLevel: LLVMCodeGenOptLevel = when { - generationState.shouldOptimize() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive - generationState.shouldContainDebugInfo() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelNone + context.shouldOptimize() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelAggressive + context.shouldContainDebugInfo() -> LLVMCodeGenOptLevel.LLVMCodeGenLevelNone else -> LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault } - val relocMode: LLVMRelocMode = configurables.currentRelocationMode(generationState).translateToLlvmRelocMode() + val relocMode: LLVMRelocMode = configurables.currentRelocationMode(context).translateToLlvmRelocMode() val codeModel: LLVMCodeModel = LLVMCodeModel.LLVMCodeModelDefault val globalDce = true // Since we are in a "closed world" internalization can be safely used // to reduce size of a bitcode with global dce. - val internalize = generationState.llvmModuleSpecification.isFinal + val internalize = closedWorld // Hidden visibility makes symbols internal when linking the binary. // When producing dynamic library, this enables stripping unused symbols from binary with -dead_strip flag, // similar to DCE enabled by internalize but later: @@ -157,13 +161,13 @@ internal fun createLTOFinalPipelineConfig(generationState: NativeGenerationState // Null value means that LLVM should use default inliner params // for the provided optimization and size level. val inlineThreshold: Int? = when { - generationState.shouldOptimize() -> tryGetInlineThreshold(generationState) - generationState.shouldContainDebugInfo() -> null + context.shouldOptimize() -> tryGetInlineThreshold(context) + context.shouldContainDebugInfo() -> null else -> null } return LlvmPipelineConfig( - generationState.llvm.targetTriple, + targetTriple, cpuModel, cpuFeatures, optimizationLevel, 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 27980fa22ea..cfbc2760f17 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 @@ -66,14 +66,15 @@ internal fun PsiToIrContext.psiToIr( val forwardDeclarationsModuleDescriptor = moduleDescriptor.allDependencyModules.firstOrNull { it.isForwardDeclarationModule } - val libraryToCacheModule = config.libraryToCache?.klib?.let { + val libraryToCache = config.libraryToCache + val libraryToCacheModule = libraryToCache?.klib?.let { moduleDescriptor.allDependencyModules.single { module -> module.konanLibrary == it } } val stdlibIsCached = stdlibModule.konanLibrary?.let { config.cachedLibraries.isLibraryCached(it) } == true val stdlibIsBeingCached = libraryToCacheModule == stdlibModule require(!(stdlibIsCached && stdlibIsBeingCached)) { "The cache for stdlib is already built" } - val kFunctionImplIsBeingCached = stdlibIsBeingCached && config.libraryToCache?.strategy.containsKFunctionImpl + val kFunctionImplIsBeingCached = stdlibIsBeingCached && libraryToCache?.strategy.containsKFunctionImpl val shouldUseLazyFunctionClasses = (stdlibIsCached || stdlibIsBeingCached) && !kFunctionImplIsBeingCached val stubGenerator = DeclarationStubGeneratorImpl( @@ -152,7 +153,7 @@ internal fun PsiToIrContext.psiToIr( while (true) { // context.config.librariesWithDependencies could change at each iteration. val dependencies = moduleDescriptor.allDependencyModules.filter { - config.librariesWithDependencies(moduleDescriptor).contains(it.konanLibrary) + config.librariesWithDependencies().contains(it.konanLibrary) } fun sortDependencies(dependencies: List): Collection { @@ -251,11 +252,13 @@ internal fun PsiToIrContext.psiToIr( module.files.forEach { it.metadata = KonanFileMetadataSource(module as KonanIrModuleFragmentImpl) } } - return PsiToIrOutput( - modules, - mainModule, - expectDescriptorToSymbol, - symbols, - if (isProducingLibrary) null else irDeserializer as KonanIrLinker - ) + return if (isProducingLibrary) { + PsiToIrOutput.ForKlib(mainModule, symbols, expectDescriptorToSymbol) + } else if (libraryToCache == null) { + PsiToIrOutput.ForBackend(modules, mainModule, symbols, irDeserializer as KonanIrLinker) + } else { + val libraryName = libraryToCache.klib.libraryName + val libraryModule = modules[libraryName] ?: error("No module for the library being cached: $libraryName") + PsiToIrOutput.ForBackend(modules.filterKeys { it != libraryName }, libraryModule, symbols, irDeserializer as KonanIrLinker) + } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt index f8c2cd285cb..f7026b240ca 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/TargetAbiInfo.kt @@ -131,3 +131,16 @@ fun KonanTarget.getARCRetainAutoreleasedReturnValueMarker(): String? = when (thi Architecture.ARM32 -> "mov\tr7, r7\t\t// marker for objc_retainAutoreleaseReturnValue" else -> null } + +val KonanTarget.abiInfo: TargetAbiInfo + get() = when { + this == KonanTarget.MINGW_X64 -> { + WindowsX64TargetAbiInfo() + } + !family.isAppleFamily && architecture == Architecture.ARM64 -> { + AAPCS64TargetAbiInfo() + } + else -> { + DefaultTargetAbiInfo() + } + } \ No newline at end of file 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 33c4f5af382..a789898b1a5 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 @@ -64,23 +64,6 @@ internal fun konanUnitPhase( op: Context.() -> Unit ) = namedOpUnitPhase(name, description, prerequisite, op) -internal val buildAdditionalCacheInfoPhase = konanUnitPhase( - op = { - val module = irModules[config.libraryToCache!!.klib.libraryName] - ?: error("No module for the library being cached: ${config.libraryToCache!!.klib.libraryName}") - val moduleDeserializer = irLinker.moduleDeserializers[module.descriptor] - if (moduleDeserializer == null) { - require(module.descriptor.isFromInteropLibrary()) { "No module deserializer for ${module.descriptor}" } - } else { - CacheInfoBuilder(this.generationState, moduleDeserializer, module).build() - } - }, - name = "BuildAdditionalCacheInfo", - description = "Build additional cache info (inline functions bodies and fields of classes)", -// prerequisites generally do not work in dynamic driver. -// prerequisite = setOf(psiToIrPhase) -) - /* * Sometimes psi2ir produces IR with non-trivial variance in super types of SAM conversions (this is a language design issue). * Earlier this was solved with just erasing all such variances but this might lead to some other hard to debug problems, @@ -88,209 +71,26 @@ internal val buildAdditionalCacheInfoPhase = konanUnitPhase( * even if they do, then it's better to throw an error right away than to dig out weird crashes down the pipeline or even at runtime. * We explicitly check this, also fixing older klibs built with previous compiler versions by applying the same trick as before. */ -internal val checkSamSuperTypesPhase = konanUnitPhase( - op = { - // Handling types in current module not recursively: - // psi2ir can produce SAM conversions with variances in type arguments of type arguments. - // See https://youtrack.jetbrains.com/issue/KT-49384. - // So don't go deeper than top-level arguments to avoid the compiler emitting false-positive errors. - // Lowerings can handle this. - // Also such variances are allowed in the language for manual implementations of interfaces. - irModule!!.files - .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.THROW, recurse = false).run() } - // TODO: This is temporary for handling klibs produced with earlier compiler versions. - // Handling types in dependencies recursively, just to be extra safe: don't change something that works. - irModules.values - .flatMap { it.files } - .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.ERASE, recurse = true).run() } - }, - name = "CheckSamSuperTypes", - description = "Check SAM conversions super types" -) +//internal val checkSamSuperTypesPhase = konanUnitPhase( +// op = { +// // Handling types in current module not recursively: +// // psi2ir can produce SAM conversions with variances in type arguments of type arguments. +// // See https://youtrack.jetbrains.com/issue/KT-49384. +// // So don't go deeper than top-level arguments to avoid the compiler emitting false-positive errors. +// // Lowerings can handle this. +// // Also such variances are allowed in the language for manual implementations of interfaces. +// irModule!!.files +// .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.THROW, recurse = false).run() } +// // TODO: This is temporary for handling klibs produced with earlier compiler versions. +// // Handling types in dependencies recursively, just to be extra safe: don't change something that works. +// irModules.values +// .flatMap { it.files } +// .forEach { SamSuperTypesChecker(this, it, mode = SamSuperTypesChecker.Mode.ERASE, recurse = true).run() } +// }, +// name = "CheckSamSuperTypes", +// description = "Check SAM conversions super types" +//) -internal val saveAdditionalCacheInfoPhase = konanUnitPhase( - op = { CacheStorage(generationState).saveAdditionalCacheInfo() }, - name = "SaveAdditionalCacheInfo", - description = "Save additional cache info (inline functions bodies and fields of classes)" -) - -internal val objectFilesPhase = konanUnitPhase( - op = { this.generationState.compilerOutput = BitcodeCompiler(this.generationState).makeObjectFiles(this.generationState.bitcodeFileName) }, - name = "ObjectFiles", - description = "Bitcode to object file" -) - -internal val linkerPhase = konanUnitPhase( - op = { Linker(this.generationState).link(this.generationState.compilerOutput) }, - name = "Linker", - description = "Linker" -) - -internal val finalizeCachePhase = konanUnitPhase( - op = { CacheStorage(this.generationState).renameOutput() }, - name = "FinalizeCache", - description = "Finalize cache (rename temp to the final dist)" -) - -internal val allLoweringsPhase = SameTypeNamedCompilerPhase( - name = "IrLowering", - description = "IR Lowering", - // TODO: The lowerings before inlinePhase should be aligned with [NativeInlineFunctionResolver.kt] - lower = performByIrFile( - name = "IrLowerByFile", - description = "IR Lowering by file", - lower = listOf( - createFileLowerStatePhase, - removeExpectDeclarationsPhase, - stripTypeAliasDeclarationsPhase, - lowerBeforeInlinePhase, - arrayConstructorPhase, - lateinitPhase, - sharedVariablesPhase, - inventNamesForLocalClasses, - extractLocalClassesFromInlineBodies, - wrapInlineDeclarationsWithReifiedTypeParametersLowering, - inlinePhase, - provisionalFunctionExpressionPhase, - postInlinePhase, - contractsDslRemovePhase, - annotationImplementationPhase, - rangeContainsLoweringPhase, - forLoopsPhase, - flattenStringConcatenationPhase, - foldConstantLoweringPhase, - computeStringTrimPhase, - stringConcatenationPhase, - stringConcatenationTypeNarrowingPhase, - enumConstructorsPhase, - initializersPhase, - localFunctionsPhase, - volatilePhase, - tailrecPhase, - defaultParameterExtentPhase, - innerClassPhase, - dataClassesPhase, - ifNullExpressionsFusionPhase, - testProcessorPhase, - delegationPhase, - functionReferencePhase, - singleAbstractMethodPhase, - enumWhenPhase, - builtinOperatorPhase, - finallyBlocksPhase, - enumClassPhase, - enumUsagePhase, - interopPhase, - varargPhase, - kotlinNothingValueExceptionPhase, - coroutinesPhase, - typeOperatorPhase, - expressionBodyTransformPhase, - objectClassesPhase, - constantInliningPhase, - staticInitializersPhase, - bridgesPhase, - exportInternalAbiPhase, - useInternalAbiPhase, - autoboxPhase, - ) - ), - actions = setOf(defaultDumper, ::moduleValidationCallback) -) - -internal val dependenciesLowerPhase = SameTypeNamedCompilerPhase( - name = "LowerLibIR", - description = "Lower library's IR", - prerequisite = emptySet(), - lower = object : CompilerPhase { - override fun invoke(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: IrModuleFragment): IrModuleFragment { - val files = mutableListOf() - files += input.files - input.files.clear() - - // TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order. - context.librariesWithDependencies - .reversed() - .forEach { - val libModule = context.irModules[it.libraryName] - if (libModule == null || !context.generationState.llvmModuleSpecification.containsModule(libModule)) - return@forEach - - input.files += libModule.files - allLoweringsPhase.invoke(phaseConfig, phaserState, context, input) - - input.files.clear() - } - - // Save all files for codegen in reverse topological order. - // This guarantees that libraries initializers are emitted in correct order. - context.librariesWithDependencies - .forEach { - val libModule = context.irModules[it.libraryName] - if (libModule == null || !context.generationState.llvmModuleSpecification.containsModule(libModule)) - return@forEach - - input.files += libModule.files - } - - input.files += files - - return input - } - }) - -internal val entryPointPhase = makeCustomPhase( - name = "addEntryPoint", - description = "Add entry point for program", - prerequisite = emptySet(), - op = { context, _ -> - require(context.config.produce == CompilerOutputKind.PROGRAM) - - val entryPoint = context.ir.symbols.entryPoint!!.owner - val file = if (context.generationState.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(NaiveSourceBasedFileEntryImpl("entryPointOwner"), FqName("kotlin.native.internal.abi")) - } - - file.addChild(makeEntryPoint(context.generationState)) - } -) - -internal val bitcodePhase = SameTypeNamedCompilerPhase( - name = "Bitcode", - description = "LLVM Bitcode generation", - lower = returnsInsertionPhase then - buildDFGPhase then - devirtualizationAnalysisPhase then - dcePhase then - removeRedundantCallsToStaticInitializersPhase then - devirtualizationPhase then - propertyAccessorInlinePhase then // Have to run after link dependencies phase, because fields - // from dependencies can be changed during lowerings. - inlineClassPropertyAccessorsPhase then - redundantCoercionsCleaningPhase then - unboxInlinePhase then - createLLVMDeclarationsPhase then - ghaPhase then - RTTIPhase then - escapeAnalysisPhase then - codegenPhase then - cStubsPhase -) - -internal val bitcodePostprocessingPhase = SameTypeNamedCompilerPhase( - name = "BitcodePostprocessing", - description = "Optimize and rewrite bitcode", - lower = checkExternalCallsPhase then - bitcodeOptimizationPhase then - coveragePhase then - removeRedundantSafepointsPhase then - optimizeTLSDataLoadsPhase then - rewriteExternalCallsCheckerGlobals -) internal fun PhaseConfigurationService.disableIf(phase: AnyNamedPhase, condition: Boolean) { if (condition) disable(phase) @@ -306,41 +106,9 @@ internal fun PhaseConfigurationService.konanPhasesConfig(config: KonanConfig) { // (which doesn't report error for these corner cases), we turn off the checker for now (the problem with variances // is workarounded in [FunctionReferenceLowering] by taking erasure of SAM conversion type). // Also see https://youtrack.jetbrains.com/issue/KT-50399 for more details. - disable(checkSamSuperTypesPhase) - - disableUnless(buildAdditionalCacheInfoPhase, config.produce.isCache) - disableUnless(saveAdditionalCacheInfoPhase, config.produce.isCache) - disableUnless(finalizeCachePhase, config.produce.isCache) +// disable(checkSamSuperTypesPhase) disableUnless(exportInternalAbiPhase, config.produce.isCache) - disableUnless(functionsWithoutBoundCheck, config.involvesCodegen) - disableUnless(checkExternalCallsPhase, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)) - disableUnless(rewriteExternalCallsCheckerGlobals, getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS)) disableUnless(stringConcatenationTypeNarrowingPhase, config.optimizationsEnabled) - disableUnless(optimizeTLSDataLoadsPhase, config.optimizationsEnabled) - if (!config.involvesLinkStage) { - disable(bitcodePostprocessingPhase) - disable(linkBitcodeDependenciesPhase) - disable(objectFilesPhase) - disable(linkerPhase) - } disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE) - if (!config.optimizationsEnabled) { - disable(buildDFGPhase) - disable(devirtualizationAnalysisPhase) - disable(devirtualizationPhase) - disable(escapeAnalysisPhase) - // Inline accessors only in optimized builds due to separate compilation and possibility to get broken - // debug information. - disable(propertyAccessorInlinePhase) - disable(unboxInlinePhase) - disable(inlineClassPropertyAccessorsPhase) - disable(dcePhase) - disable(removeRedundantCallsToStaticInitializersPhase) - disable(ghaPhase) - } - disableUnless(verifyBitcodePhase, config.needCompilerVerification || getBoolean(KonanConfigKeys.VERIFY_BITCODE)) - - - disableUnless(removeRedundantSafepointsPhase, config.memoryModel == MemoryModel.EXPERIMENTAL) } } 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 59714964c38..81dc38f20b7 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 @@ -263,8 +263,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod } } -internal fun IrField.requiredAlignment(context: Context) : Int { - val llvm = context.generationState.llvm +internal fun IrField.requiredAlignment(llvm: Llvm): Int { val llvmType = type.toLLVMType(llvm) val abiAlignment = if (llvmType == llvm.vector128Type) { 8 // over-aligned objects are not supported now, and this worked somehow, so let's keep it as it for now @@ -284,10 +283,10 @@ internal fun IrField.requiredAlignment(context: Context) : Int { internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { - private fun IrField.toFieldInfo(): FieldInfo { + private fun IrField.toFieldInfo(llvm: Llvm): FieldInfo { val isConst = correspondingPropertySymbol?.owner?.isConst ?: false require(!isConst || initializer?.expression is IrConst<*>) { "A const val field ${render()} must have constant initializer" } - return FieldInfo(name.asString(), type, isConst, symbol, requiredAlignment(context)) + return FieldInfo(name.asString(), type, isConst, symbol, requiredAlignment(llvm)) } val vtableEntries: List by lazy { @@ -441,7 +440,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { if (mappedField == fieldInfo.irField) fieldInfo else - mappedField!!.toFieldInfo() + mappedField!!.toFieldInfo(llvm) } private var fields: List? = null @@ -452,7 +451,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { val superClass = irClass.getSuperClassNotAny() val superFields = if (superClass != null) context.getLayoutBuilder(superClass).getFieldsInternal(llvm) else emptyList() - val declaredFields = getDeclaredFields() + val declaredFields = getDeclaredFields(llvm) val sortedDeclaredFields = if (irClass.hasAnnotation(KonanFqNames.noReorderFields)) declaredFields else @@ -504,7 +503,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { /** * Fields declared in the class. */ - fun getDeclaredFields(): List { + fun getDeclaredFields(llvm: Llvm): List { val outerThisField = if (irClass.isInner) context.innerClassesSupport.getOuterThisField(irClass) else null @@ -517,7 +516,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { require(context.config.cachedLibraries.isLibraryCached(moduleDeserializer.klib)) { "No IR and no cache for ${irClass.render()}" } - return moduleDeserializer.deserializeClassFields(irClass, outerThisField?.toFieldInfo()) + return moduleDeserializer.deserializeClassFields(irClass, outerThisField?.toFieldInfo(llvm)) } val declarations = irClass.declarations.toMutableList() @@ -527,8 +526,8 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { } return declarations.mapNotNull { when (it) { - is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo() - is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo() + is IrField -> it.takeIf { it.isReal && !it.isStatic }?.toFieldInfo(llvm) + is IrProperty -> it.takeIf { it.isReal }?.backingField?.takeIf { !it.isStatic }?.toFieldInfo(llvm) else -> null } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt index 7ade71f733b..45dab643ff7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/DescriptorUtils.kt @@ -10,7 +10,6 @@ import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.ir.getSuperClassNotAny import org.jetbrains.kotlin.backend.konan.ir.getSuperInterfaces import org.jetbrains.kotlin.backend.konan.llvm.isVoidAsReturnType -import org.jetbrains.kotlin.backend.konan.llvm.longName import org.jetbrains.kotlin.backend.konan.lower.erasedUpperBound import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor @@ -278,4 +277,7 @@ fun IrFunction.externalSymbolOrThrow(): String? { throw Error("external function ${this.longName} must have @TypedIntrinsic, @SymbolName, @GCUnsafeCall or @ObjCMethod annotation") } +private val IrFunction.longName: String + get() = "${(parent as? IrClass)?.name?.asString() ?: ""}.${(this as? IrSimpleFunction)?.name ?: ""}" + val IrFunction.isBuiltInOperator get() = origin == IrBuiltIns.BUILTIN_OPERATOR diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt index 131162e4b16..a09a9ad04d8 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/DynamicCompilerDriver.kt @@ -9,6 +9,9 @@ import kotlinx.cinterop.usingJvmCInteropCallbacks import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.KonanConfig import org.jetbrains.kotlin.backend.konan.driver.phases.* +import org.jetbrains.kotlin.backend.konan.getIncludedLibraryDescriptors +import org.jetbrains.kotlin.backend.konan.isCache +import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys import org.jetbrains.kotlin.konan.target.CompilerOutputKind @@ -55,11 +58,12 @@ internal class DynamicCompilerDriver : CompilerDriver() { val (psiToIrOutput, objCCodeSpec) = engine.runPsiToIr(frontendOutput, isProducingLibrary = false) { it.runPhase(CreateObjCExportCodeSpecPhase, objCExportedInterface) } + require(psiToIrOutput is PsiToIrOutput.ForBackend) val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput) { it.objCExportedInterface = objCExportedInterface it.objCExportCodeSpec = objCCodeSpec } - engine.runBackend(backendContext) + engine.runBackend(backendContext, psiToIrOutput.irModule) } private fun produceCLibrary(engine: PhaseEngine, config: KonanConfig, environment: KotlinCoreEnvironment) { @@ -67,10 +71,11 @@ internal class DynamicCompilerDriver : CompilerDriver() { val (psiToIrOutput, cAdapterElements) = engine.runPsiToIr(frontendOutput, isProducingLibrary = false) { it.runPhase(BuildCExports, frontendOutput) } + require(psiToIrOutput is PsiToIrOutput.ForBackend) val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput) { it.cAdapterExportedElements = cAdapterElements } - engine.runBackend(backendContext) + engine.runBackend(backendContext, psiToIrOutput.irModule) } private fun produceKlib(engine: PhaseEngine, config: KonanConfig, environment: KotlinCoreEnvironment) { @@ -105,6 +110,7 @@ internal class DynamicCompilerDriver : CompilerDriver() { } else { engine.runPsiToIr(frontendOutput, isProducingLibrary = true) } + require(psiToIrOutput is PsiToIrOutput.ForKlib) return engine.runSerializer(frontendOutput.moduleDescriptor, psiToIrOutput) } @@ -114,22 +120,25 @@ internal class DynamicCompilerDriver : CompilerDriver() { private fun produceBinary(engine: PhaseEngine, config: KonanConfig, environment: KotlinCoreEnvironment) { val frontendOutput = engine.runFrontend(config, environment) ?: return val psiToIrOutput = engine.runPsiToIr(frontendOutput, isProducingLibrary = false) + require(psiToIrOutput is PsiToIrOutput.ForBackend) val backendContext = createBackendContext(config, frontendOutput, psiToIrOutput) - engine.runBackend(backendContext) + engine.runBackend(backendContext, psiToIrOutput.irModule) } private fun createBackendContext( config: KonanConfig, frontendOutput: FrontendPhaseOutput.Full, - psiToIrOutput: PsiToIrOutput, + psiToIrOutput: PsiToIrOutput.ForBackend, additionalDataSetter: (Context) -> Unit = {} ) = Context( config, - frontendOutput.environment, - frontendOutput.bindingContext, - frontendOutput.moduleDescriptor + frontendOutput.moduleDescriptor.getIncludedLibraryDescriptors(config).toSet() + frontendOutput.moduleDescriptor, + frontendOutput.moduleDescriptor.builtIns as KonanBuiltIns, + psiToIrOutput.irModule.irBuiltins, + psiToIrOutput.irModules, + psiToIrOutput.irLinker, + psiToIrOutput.symbols ).also { - it.populateAfterPsiToIr(psiToIrOutput) additionalDataSetter(it) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/Machinery.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/Machinery.kt index ec54b32df3c..7b7ae052c1a 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/Machinery.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/Machinery.kt @@ -122,12 +122,17 @@ internal class PhaseEngine( fun > runPhase( phase: P, - input: Input + input: Input, + disable: Boolean = false ): Output { + if (disable) { + return phase.outputIfNotEnabled(phaseConfig, phaserState.changePhaserStateType(), context, input) + } // We lose sticky postconditions here, but it should be ok, since type is changed. return phase.invoke(phaseConfig, phaserState.changePhaserStateType(), context, input) } + fun > runPhase( phase: P, ): Output = runPhase(phase, Unit) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt index fa1304c0aaa..6fe580acaf0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BackendPhases.kt @@ -7,14 +7,28 @@ package org.jetbrains.kotlin.backend.konan.driver.phases import org.jetbrains.kotlin.backend.common.lower import org.jetbrains.kotlin.backend.konan.InteropBuiltIns +import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine +import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.lower.ExpectToActualDefaultValueCopier import org.jetbrains.kotlin.backend.konan.lower.SpecialBackendChecksTraversal import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns +import org.jetbrains.kotlin.backend.konan.makeEntryPoint +import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.util.NaiveSourceBasedFileEntryImpl +import org.jetbrains.kotlin.ir.util.addChild +import org.jetbrains.kotlin.ir.util.addFile +import org.jetbrains.kotlin.ir.util.file +import org.jetbrains.kotlin.name.FqName -internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase( +internal data class SpecialBackendChecksInput( + val irModule: IrModuleFragment, + val symbols: KonanSymbols, +) + +internal val SpecialBackendChecksPhase = createSimpleNamedCompilerPhase( "SpecialBackendChecks", "Special backend checks", ) { context, input -> @@ -38,13 +52,30 @@ internal val CopyDefaultValuesToActualPhase = createSimpleNamedCompilerPhase - ExpectToActualDefaultValueCopier(input).process() + ExpectToActualDefaultValueCopier(input).process() } -internal fun PhaseEngine.runSpecialBackendChecks(psiToIrOutput: PsiToIrOutput) { - runPhase(SpecialBackendChecksPhase, psiToIrOutput) +internal fun PhaseEngine.runSpecialBackendChecks(irModule: IrModuleFragment, symbols: KonanSymbols) { + runPhase(SpecialBackendChecksPhase, SpecialBackendChecksInput(irModule, symbols)) } internal fun PhaseEngine.runK2SpecialBackendChecks(fir2IrOutput: Fir2IrOutput) { runPhase(K2SpecialBackendChecksPhase, fir2IrOutput) +} + +internal val EntryPointPhase = createSimpleNamedCompilerPhase( + name = "addEntryPoint", + description = "Add entry point for program" +) { context, module -> + val parent = context.context + val entryPoint = parent.ir.symbols.entryPoint!!.owner + val file: IrFile = 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. + module.addFile(NaiveSourceBasedFileEntryImpl("entryPointOwner"), FqName("kotlin.native.internal.abi")) + } + + file.addChild(makeEntryPoint(context)) } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt index e2847ccdc8b..0035bc73685 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Bitcode.kt @@ -5,9 +5,29 @@ package org.jetbrains.kotlin.backend.konan.driver.phases +import llvm.LLVMDumpModule +import llvm.LLVMModuleRef import llvm.LLVMWriteBitcodeToFile +import org.jetbrains.kotlin.backend.common.phaser.Action +import org.jetbrains.kotlin.backend.common.phaser.ActionState +import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.checkLlvmModuleExternalCalls +import org.jetbrains.kotlin.backend.konan.createLTOFinalPipelineConfig +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine import org.jetbrains.kotlin.backend.konan.insertAliasToEntryPoint +import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass +import org.jetbrains.kotlin.backend.konan.llvm.verifyModule +import org.jetbrains.kotlin.backend.konan.optimizations.RemoveRedundantSafepointsPass +import org.jetbrains.kotlin.backend.konan.optimizations.removeMultipleThreadDataLoads + +private val nativeLLVMDumper = + fun(actionState: ActionState, _: Unit, context: NativeGenerationState) { + llvmIrDumpCallback(actionState, context.llvm.module, context.config, context.tempFiles) + } + +private val llvmPhaseActions: Set> = setOf(nativeLLVMDumper) /** * Write in-memory LLVM module to filesystem as a bitcode. @@ -16,14 +36,111 @@ import org.jetbrains.kotlin.backend.konan.insertAliasToEntryPoint * after static driver removal. */ -internal val WriteBitcodeFilePhase = createSimpleNamedCompilerPhase( +internal val WriteBitcodeFilePhase = createSimpleNamedCompilerPhase( "WriteBitcodeFile", "Write bitcode file", - outputIfNotEnabled = { _, _, _, _ -> } -) { context: NativeGenerationState, _: Unit -> + outputIfNotEnabled = { _, _, _, _ -> error("WriteBitcodeFile be disabled") } +) { context, llvmModule -> val output = context.tempFiles.nativeBinaryFileName - context.bitcodeFileName = output // Insert `_main` after pipeline, so we won't worry about optimizations corrupting entry point. insertAliasToEntryPoint(context) - LLVMWriteBitcodeToFile(context.llvm.module, output) + LLVMWriteBitcodeToFile(llvmModule, output) + output +} + +internal val CheckExternalCallsPhase = createSimpleNamedCompilerPhase( + name = "CheckExternalCalls", + description = "Check external calls", + postactions = llvmPhaseActions, +) { context, _ -> + checkLlvmModuleExternalCalls(context) +} + +internal val RewriteExternalCallsCheckerGlobals = createSimpleNamedCompilerPhase( + name = "RewriteExternalCallsCheckerGlobals", + description = "Rewrite globals for external calls checker after optimizer run", + postactions = llvmPhaseActions, +) { context, _ -> + addFunctionsListSymbolForChecker(context) +} + +internal val BitcodeOptimizationPhase = createSimpleNamedCompilerPhase( + name = "BitcodeOptimization", + description = "Optimize bitcode", + postactions = llvmPhaseActions, +) { context, _ -> + val config = createLTOFinalPipelineConfig(context, context.llvm.targetTriple, closedWorld = context.llvmModuleSpecification.isFinal) + LlvmOptimizationPipeline(config, context.llvm.module, context).use { + it.run() + } +} + +internal val CoveragePhase = createSimpleNamedCompilerPhase( + name = "Coverage", + description = "Produce coverage information", + postactions = llvmPhaseActions, + op = { context, _ -> runCoveragePass(context) } +) + +internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase( + name = "RemoveRedundantSafepoints", + description = "Remove function prologue safepoints inlined to another function", + postactions = llvmPhaseActions, + op = { context, _ -> + RemoveRedundantSafepointsPass().runOnModule( + module = context.llvm.module, + isSafepointInliningAllowed = context.shouldInlineSafepoints() + ) + } +) + +internal val OptimizeTLSDataLoadsPhase = createSimpleNamedCompilerPhase( + name = "OptimizeTLSDataLoads", + description = "Optimize multiple loads of thread data", + postactions = llvmPhaseActions, + op = { context, _ -> removeMultipleThreadDataLoads(context) } +) + +internal val CStubsPhase = createSimpleNamedCompilerPhase( + name = "CStubs", + description = "C stubs compilation", + postactions = llvmPhaseActions, + op = { context, _ -> produceCStubs(context) } +) + +internal val LinkBitcodeDependenciesPhase = createSimpleNamedCompilerPhase( + name = "LinkBitcodeDependencies", + description = "Link bitcode dependencies", + postactions = llvmPhaseActions, + op = { context, _ -> linkBitcodeDependencies(context) } +) + +internal val VerifyBitcodePhase = createSimpleNamedCompilerPhase( + name = "VerifyBitcode", + description = "Verify bitcode", + op = { _, llvmModule -> verifyModule(llvmModule) } +) + +internal val PrintBitcodePhase = createSimpleNamedCompilerPhase( + name = "PrintBitcode", + description = "Print bitcode", + op = { _, llvmModule -> LLVMDumpModule(llvmModule) } +) + +internal fun PhaseEngine.runBitcodePostProcessing() { + val checkExternalCalls = context.config.configuration.getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS) + if (checkExternalCalls) { + runPhase(CheckExternalCallsPhase) + } + runPhase(BitcodeOptimizationPhase) + runPhase(CoveragePhase) + if (context.config.memoryModel == MemoryModel.EXPERIMENTAL) { + runPhase(RemoveRedundantSafepointsPhase) + } + if (context.config.optimizationsEnabled) { + runPhase(OptimizeTLSDataLoadsPhase) + } + if (checkExternalCalls) { + runPhase(RewriteExternalCallsCheckerGlobals) + } } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BitcodeGeneration.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BitcodeGeneration.kt new file mode 100644 index 00000000000..223b31f3a27 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/BitcodeGeneration.kt @@ -0,0 +1,65 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import llvm.DIFinalize +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.llvm.CodeGeneratorVisitor +import org.jetbrains.kotlin.backend.konan.llvm.Lifetime +import org.jetbrains.kotlin.backend.konan.llvm.RTTIGeneratorVisitor +import org.jetbrains.kotlin.backend.konan.llvm.createLlvmDeclarations +import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.visitors.acceptVoid + +internal val CreateLLVMDeclarationsPhase = createSimpleNamedCompilerPhase( + name = "CreateLLVMDeclarations", + description = "Map IR declarations to LLVM", + op = { generationState, module -> + generationState.llvmDeclarations = createLlvmDeclarations(generationState, module) + } +) + +internal data class RTTIInput( + val irModule: IrModuleFragment, + val referencedFunctions: Set? +) + +internal val RTTIPhase = createSimpleNamedCompilerPhase( + name = "RTTI", + description = "RTTI generation", + op = { generationState, input -> + val visitor = RTTIGeneratorVisitor(generationState, input.referencedFunctions) + input.irModule.acceptVoid(visitor) + visitor.dispose() + } +) + +internal data class CodegenInput( + val irModule: IrModuleFragment, + val lifetimes: Map +) + +internal val CodegenPhase = createSimpleNamedCompilerPhase( + name = "Codegen", + description = "Code generation", + op = { generationState, input -> + val context = generationState.context + generationState.objCExport = ObjCExport( + generationState, + input.irModule.descriptor, + context.objCExportedInterface, + context.objCExportCodeSpec + ) + + input.irModule.acceptVoid(CodeGeneratorVisitor(generationState, input.irModule.irBuiltins, input.lifetimes)) + + if (generationState.hasDebugInfo()) + DIFinalize(generationState.debugInfo.builder) + } +) \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CacheBuilding.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CacheBuilding.kt new file mode 100644 index 00000000000..47baf67c3dd --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/CacheBuilding.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.konan.CacheStorage +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.OutputFiles +import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.lower.CacheInfoBuilder +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment + +internal val BuildAdditionalCacheInfoPhase = createSimpleNamedCompilerPhase( + name = "BuildAdditionalCacheInfo", + description = "Build additional cache info (inline functions bodies and fields of classes)", +) { context, module -> + // TODO: Use explicit parameter + val parent = context.context + val moduleDeserializer = parent.irLinker.moduleDeserializers[module.descriptor] + if (moduleDeserializer == null) { + require(module.descriptor.isFromInteropLibrary()) { "No module deserializer for ${module.descriptor}" } + } else { + CacheInfoBuilder(context, moduleDeserializer, module).build() + } +} + +/** + * It is naturally a part of "produce LLVM module", so using NativeGenerationState context should be OK. + */ +internal val SaveAdditionalCacheInfoPhase = createSimpleNamedCompilerPhase( + name = "SaveAdditionalCacheInfo", + description = "Save additional cache info (inline functions bodies and fields of classes)" +) { context, _ -> + // TODO: Extract necessary parts of context into explicit input. + CacheStorage(context).saveAdditionalCacheInfo() +} + +internal val FinalizeCachePhase = createSimpleNamedCompilerPhase( + name = "FinalizeCache", + description = "Finalize cache (rename temp to the final dist)" +) { _, outputFiles -> + // TODO: Explicit parameter + CacheStorage.renameOutput(outputFiles) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Frontend.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Frontend.kt index 86e250f4aea..e29acdfd96c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Frontend.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Frontend.kt @@ -75,8 +75,4 @@ internal val FrontendPhase = createSimpleNamedCompilerPhase( } else { FrontendPhaseOutput.ShouldNotGenerateCode } -} - -internal fun PhaseEngine.runFrontend(environment: KotlinCoreEnvironment): FrontendPhaseOutput { - return this.runPhase(FrontendPhase, environment) } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/LTO.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/LTO.kt new file mode 100644 index 00000000000..e2b0752d0e1 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/LTO.kt @@ -0,0 +1,179 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.common.phaser.ActionState +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis +import org.jetbrains.kotlin.backend.konan.llvm.Lifetime +import org.jetbrains.kotlin.backend.konan.optimizations.* +import org.jetbrains.kotlin.backend.konan.optimizations.DevirtualizationAnalysis +import org.jetbrains.kotlin.backend.konan.optimizations.ExternalModulesDFG +import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFG +import org.jetbrains.kotlin.backend.konan.optimizations.ModuleDFGBuilder +import org.jetbrains.kotlin.backend.konan.optimizations.dce +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.declarations.IrFunction +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment + +internal val GHAPhase = createSimpleNamedCompilerPhase( + name = "GHAPhase", + description = "Global hierarchy analysis", + op = { generationState, irModule -> + GlobalHierarchyAnalysis(generationState.context, irModule).run() + } +) + +internal val BuildDFGPhase = createSimpleNamedCompilerPhase( + name = "BuildDFG", + description = "Data flow graph building", + outputIfNotEnabled = { _, _, generationState, irModule -> + val context = generationState.context + val symbolTable = DataFlowIR.SymbolTable(context, DataFlowIR.Module(irModule.descriptor)) + ModuleDFG(emptyMap(), symbolTable) + }, + op = { generationState, irModule -> + val context = generationState.context + ModuleDFGBuilder(context, irModule).build() + } +) + +internal data class DevirtualizationAnalysisInput( + val irModule: IrModuleFragment, + val moduleDFG: ModuleDFG, +) + +internal val DevirtualizationAnalysisPhase = createSimpleNamedCompilerPhase( + name = "DevirtualizationAnalysis", + description = "Devirtualization analysis", + outputIfNotEnabled = { _, _, _, _ -> + DevirtualizationAnalysis.AnalysisResult( + emptyMap(), + DevirtualizationAnalysis.DevirtualizationAnalysisImpl.EmptyTypeHierarchy + ) + }, + op = { generationState, (irModule, moduleDFG) -> + val context = generationState.context + val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) + DevirtualizationAnalysis.run(context, irModule, moduleDFG, externalModulesDFG) + } +) + +internal data class DCEInput( + val irModule: IrModuleFragment, + val moduleDFG: ModuleDFG, + val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult, +) + +internal val DCEPhase = createSimpleNamedCompilerPhase?>( + name = "DCEPhase", + description = "Dead code elimination", + outputIfNotEnabled = { _, _, _, _ -> null }, + op = { generationState, input -> + val context = generationState.context + dce(context, input.irModule, input.moduleDFG, input.devirtualizationAnalysisResult) + } +) + +internal data class DevirtualizationInput( + val irModule: IrModuleFragment, + val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult +) + +internal val DevirtualizationPhase = createSimpleNamedCompilerPhase( + name = "Devirtualization", + description = "Devirtualization", + postactions = modulePhaseActions.map { f -> + fun(actionState: ActionState, data: DevirtualizationInput, context: NativeGenerationState) = + f(actionState, data.irModule, context) + }.toSet(), + op = { generationState, input -> + val context = generationState.context + val devirtualizedCallSites = input.devirtualizationAnalysisResult.devirtualizedCallSites + .asSequence() + .filter { it.key.irCallSite != null } + .associate { it.key.irCallSite!! to it.value } + val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) + DevirtualizationAnalysis.devirtualize(input.irModule, context, + externalModulesDFG, devirtualizedCallSites) + } +) + +internal data class EscapeAnalysisInput( + val irModule: IrModuleFragment, + val moduleDFG: ModuleDFG, + val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult, +) + +internal val EscapeAnalysisPhase = createSimpleNamedCompilerPhase>( + name = "EscapeAnalysis", + description = "Escape analysis", + outputIfNotEnabled = { _, _, _, _ -> emptyMap() }, + op = { generationState, input -> + val lifetimes = mutableMapOf() + val context = generationState.context + val entryPoint = context.ir.symbols.entryPoint?.owner + val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) + val nonDevirtualizedCallSitesUnfoldFactor = + if (entryPoint != null) { + // For a final program it can be safely assumed that what classes we see is what we got, + // so can take those. In theory we can always unfold call sites using type hierarchy, but + // the analysis might converge much, much slower, so take only reasonably small for now. + 5 + } else { + // Can't tolerate any non-devirtualized call site for a library. + // TODO: What about private virtual functions? + // Note: 0 is also bad - this means that there're no inheritors in the current source set, + // but there might be some provided by the users of the library being produced. + -1 + } + val callGraph = CallGraphBuilder( + context, + input.irModule, + input.moduleDFG, + externalModulesDFG, + input.devirtualizationAnalysisResult, + nonDevirtualizedCallSitesUnfoldFactor + ).build() + EscapeAnalysis.computeLifetimes(context, generationState, input.moduleDFG, externalModulesDFG, callGraph, lifetimes) + lifetimes + } +) + +internal data class RedundantCallsInput( + val moduleDFG: ModuleDFG, + val devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult, + val irModule: IrModuleFragment, +) + +internal val RemoveRedundantCallsToStaticInitializersPhase = createSimpleNamedCompilerPhase( + name = "RemoveRedundantCallsToStaticInitializersPhase", + description = "Redundant static initializers calls removal", + postactions = modulePhaseActions.map { f -> + fun(actionState: ActionState, data: RedundantCallsInput, context: NativeGenerationState) = + f(actionState, data.irModule, context) + }.toSet(), + op = { generationState, input -> + val context = generationState.context + val moduleDFG = input.moduleDFG + val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) + + val callGraph = CallGraphBuilder( + context, + input.irModule, + moduleDFG, + externalModulesDFG, + input.devirtualizationAnalysisResult, + nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE + ).build() + + val rootSet = DevirtualizationAnalysis.computeRootSet(context, input.irModule, moduleDFG, externalModulesDFG) + .mapNotNull { it.irFunction } + .toSet() + + StaticInitializersOptimization.removeRedundantCalls(context, input.irModule, callGraph, rootSet) + } +) \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Linker.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Linker.kt new file mode 100644 index 00000000000..6ba4720b3ac --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Linker.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.Linker +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.konan.TempFiles + +data class LinkerPhaseInput( + val outputFile: String, + val objectFiles: List, + val dependenciesTrackingResult: DependenciesTrackingResult, + val outputFiles: OutputFiles, + val temporaryFiles: TempFiles, + val isCoverageEnabled: Boolean, +) + +internal val LinkerPhase = createSimpleNamedCompilerPhase( + name = "Linker", + description = "Linker" +) { context, input -> + val linker = Linker(context, input.isCoverageEnabled, input.temporaryFiles, input.outputFiles) + linker.link(input.outputFile, input.objectFiles, input.dependenciesTrackingResult) +} \ No newline at end of file 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 new file mode 100644 index 00000000000..9c70b72256b --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Lowerings.kt @@ -0,0 +1,246 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.common.FileLoweringPass +import org.jetbrains.kotlin.backend.common.lower.coroutines.AddContinuationToNonLocalSuspendFunctionsLowering +import org.jetbrains.kotlin.backend.common.lower.inline.FunctionInlining +import org.jetbrains.kotlin.backend.common.lower.optimizations.PropertyAccessorInlineLowering +import org.jetbrains.kotlin.backend.common.phaser.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine +import org.jetbrains.kotlin.ir.declarations.IrFile +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.lower.* +import org.jetbrains.kotlin.backend.konan.lower.ImportCachesAbiTransformer +import org.jetbrains.kotlin.backend.konan.lower.InlineClassPropertyAccessorsLowering +import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner +import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering +import org.jetbrains.kotlin.backend.konan.lower.UnboxInlineLowering + +/** + * Run whole IR lowering pipeline over [irModuleFragment]. + */ +internal fun PhaseEngine.runAllLowerings(irModuleFragment: IrModuleFragment) { + val lowerings = getAllLowerings() + irModuleFragment.files.forEach { file -> + context.fileLowerState = FileLowerState() + lowerings.fold(file) { loweredFile, lowering -> + runPhase(lowering, loweredFile) + } + } +} + +private val validateAll = false + +private val fileLoweringActions: Set> = setOfNotNull( + nativeStateDumper, + nativeStateIrValidator.takeIf { validateAll } +) + +internal val modulePhaseActions: Set> = setOfNotNull( + nativeStateDumper, + nativeStateIrValidator.takeIf { validateAll } +) + +internal val ReturnsInsertionPhase = createSimpleNamedCompilerPhase( + name = "ReturnsInsertion", + description = "Returns insertion for Unit functions", + postactions = fileLoweringActions, + //prerequisite = setOf(autoboxPhase, coroutinesPhase, enumClassPhase), TODO: if there are no files in the module, this requirement fails. + op = { context, irFile -> ReturnsInsertionLowering(context.context).lower(irFile) } +) + +internal val InlineClassPropertyAccessorsPhase = createSimpleNamedCompilerPhase( + name = "InlineClassPropertyAccessorsLowering", + description = "Inline class property accessors", + postactions = fileLoweringActions, + op = { context, irFile -> InlineClassPropertyAccessorsLowering(context.context).lower(irFile) } +) + +internal val RedundantCoercionsCleaningPhase = createSimpleNamedCompilerPhase( + name = "RedundantCoercionsCleaning", + description = "Redundant coercions cleaning", + postactions = fileLoweringActions, + op = { context, irFile -> RedundantCoercionsCleaner(context.context).lower(irFile) } +) + +internal val PropertyAccessorInlinePhase = createSimpleNamedCompilerPhase( + name = "PropertyAccessorInline", + description = "Property accessor inline lowering", + postactions = fileLoweringActions +) { context, irFile -> + PropertyAccessorInlineLowering(context.context).lower(irFile) +} + +internal val UnboxInlinePhase = createSimpleNamedCompilerPhase( + name = "UnboxInline", + description = "Unbox functions inline lowering", + postactions = fileLoweringActions +) { context, irFile -> + UnboxInlineLowering(context.context).lower(irFile) +} + +private val InlinePhase = createFileLoweringPhase( + lowering = { context -> + object : FileLoweringPass { + override fun lower(irFile: IrFile) { + FunctionInlining(context.context, NativeInlineFunctionResolver(context.context, context)).lower(irFile) + } + } + }, + name = "Inline", + description = "Functions inlining", +// prerequisite = setOf(lowerBeforeInlinePhase, arrayConstructorPhase, extractLocalClassesFromInlineBodies) +) + +private val DelegationPhase = createFileLoweringPhase( + lowering = ::PropertyDelegationLowering, + name = "Delegation", + description = "Delegation lowering" +// prerequisite = setOf(volatilePhase) +) + +private val FunctionReferencePhase = createFileLoweringPhase( + lowering = ::FunctionReferenceLowering, + name = "FunctionReference", + description = "Function references lowering", +// prerequisite = setOf(delegationPhase, localFunctionsPhase) // TODO: make weak dependency on `testProcessorPhase` +) + +private val InventNamesForLocalClasses = createFileLoweringPhase( + lowering = ::NativeInventNamesForLocalClasses, + name = "InventNamesForLocalClasses", + description = "Invent names for local classes and anonymous objects", +) + +private val UseInternalAbiPhase = createSimpleNamedCompilerPhase( + name = "UseInternalAbi", + description = "Use internal ABI functions to access private entities", + outputIfNotEnabled = { _, _, _, irFile -> irFile }, +) { context, file -> + ImportCachesAbiTransformer(context).lower(file) + file +} + + +private val ObjectClassesPhase = createFileLoweringPhase( + lowering = ::ObjectClassLowering, + name = "ObjectClasses", + description = "Object classes lowering" +) + +private val CoroutinesPhase = createFileLoweringPhase( + lowering = { context -> + object : FileLoweringPass { + override fun lower(irFile: IrFile) { + NativeSuspendFunctionsLowering(context).lower(irFile) + AddContinuationToNonLocalSuspendFunctionsLowering(context.context).lower(irFile) + NativeAddContinuationToFunctionCallsLowering(context.context).lower(irFile) + AddFunctionSupertypeToSuspendFunctionLowering(context.context).lower(irFile) + } + } + }, + name = "Coroutines", + description = "Coroutines lowering", +// prerequisite = setOf(localFunctionsPhase, finallyBlocksPhase, kotlinNothingValueExceptionPhase) +) + +private val InteropPhase = createFileLoweringPhase( + lowering = ::InteropLowering, + name = "Interop", + description = "Interop lowering", +// prerequisite = setOf(inlinePhase, localFunctionsPhase, functionReferencePhase) +) + +private fun PhaseEngine.getAllLowerings() = listOf>( + convertToNativeGeneration(removeExpectDeclarationsPhase), + convertToNativeGeneration(stripTypeAliasDeclarationsPhase), + convertToNativeGeneration(lowerBeforeInlinePhase), + convertToNativeGeneration(arrayConstructorPhase), + convertToNativeGeneration(lateinitPhase), + convertToNativeGeneration(sharedVariablesPhase), + InventNamesForLocalClasses, + convertToNativeGeneration(extractLocalClassesFromInlineBodies), + convertToNativeGeneration(wrapInlineDeclarationsWithReifiedTypeParametersLowering), + InlinePhase, + convertToNativeGeneration(provisionalFunctionExpressionPhase), + convertToNativeGeneration(postInlinePhase), + convertToNativeGeneration(contractsDslRemovePhase), + convertToNativeGeneration(annotationImplementationPhase), + convertToNativeGeneration(rangeContainsLoweringPhase), + convertToNativeGeneration(forLoopsPhase), + convertToNativeGeneration(flattenStringConcatenationPhase), + convertToNativeGeneration(foldConstantLoweringPhase), + convertToNativeGeneration(computeStringTrimPhase), + convertToNativeGeneration(stringConcatenationPhase), + convertToNativeGeneration(stringConcatenationTypeNarrowingPhase), + convertToNativeGeneration(enumConstructorsPhase), + convertToNativeGeneration(initializersPhase), + convertToNativeGeneration(localFunctionsPhase), + convertToNativeGeneration(volatilePhase), + convertToNativeGeneration(tailrecPhase), + convertToNativeGeneration(defaultParameterExtentPhase), + convertToNativeGeneration(innerClassPhase), + convertToNativeGeneration(dataClassesPhase), + convertToNativeGeneration(ifNullExpressionsFusionPhase), + convertToNativeGeneration(testProcessorPhase), + DelegationPhase, + FunctionReferencePhase, + convertToNativeGeneration(singleAbstractMethodPhase), + convertToNativeGeneration(enumWhenPhase), + convertToNativeGeneration(builtinOperatorPhase), + convertToNativeGeneration(finallyBlocksPhase), + convertToNativeGeneration(enumClassPhase), + convertToNativeGeneration(enumUsagePhase), + InteropPhase, + convertToNativeGeneration(varargPhase), + convertToNativeGeneration(kotlinNothingValueExceptionPhase), + CoroutinesPhase, + convertToNativeGeneration(typeOperatorPhase), + convertToNativeGeneration(expressionBodyTransformPhase), + ObjectClassesPhase, + convertToNativeGeneration(constantInliningPhase), + convertToNativeGeneration(staticInitializersPhase), + convertToNativeGeneration(bridgesPhase), + convertToNativeGeneration(exportInternalAbiPhase), + UseInternalAbiPhase, + convertToNativeGeneration(autoboxPhase), +) + +/** + * Simplify porting of lowerings from static driver (where lowerings were executed in one big Context) + * to dynamic with NativeGenerationState. It can be done manual rewriting, but it is an enormous work. + */ +private fun PhaseEngine.convertToNativeGeneration( + phase: SameTypeNamedCompilerPhase, +): SimpleNamedCompilerPhase { + return createSimpleNamedCompilerPhase( + phase.name, + phase.description, + postactions = fileLoweringActions, + outputIfNotEnabled = { _, _, _, irFile -> irFile }, + op = { context, irFile -> phase.phaseBody(phaseConfig, phaserState.changePhaserStateType(), context.context, irFile) } + ) +} + +private fun createFileLoweringPhase( + name: String, + description: String, + lowering: (NativeGenerationState) -> FileLoweringPass +): SimpleNamedCompilerPhase = createSimpleNamedCompilerPhase( + name, + description, + postactions = fileLoweringActions, + outputIfNotEnabled = { _, _, _, irFile -> irFile }, + op = { context, irFile -> + lowering(context).lower(irFile) + irFile + } +) + diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/ObjectFiles.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/ObjectFiles.kt new file mode 100644 index 00000000000..d919e118cc8 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/ObjectFiles.kt @@ -0,0 +1,24 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.konan.BitcodeCompiler +import org.jetbrains.kotlin.backend.konan.ObjectFile +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.konan.TempFiles + +internal data class ObjectFilesPhaseInput( + val bitcodeFileName: String, + val temporaryFiles: TempFiles +) + +internal val ObjectFilesPhase = createSimpleNamedCompilerPhase>( + name = "ObjectFiles", + description = "Bitcode to object file", + outputIfNotEnabled = { _, _, _, _ -> emptyList() } +) { context, input -> + BitcodeCompiler(context, input.temporaryFiles).makeObjectFiles(input.bitcodeFileName) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PhaseBuilders.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PhaseBuilders.kt index 98b5f8e154a..863ffe917af 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PhaseBuilders.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PhaseBuilders.kt @@ -6,31 +6,47 @@ package org.jetbrains.kotlin.backend.konan.driver.phases import org.jetbrains.kotlin.backend.common.LoggingContext -import org.jetbrains.kotlin.backend.common.phaser.PhaseConfigurationService -import org.jetbrains.kotlin.backend.common.phaser.PhaserState -import org.jetbrains.kotlin.backend.common.phaser.SimpleNamedCompilerPhase +import org.jetbrains.kotlin.backend.common.phaser.* internal fun createSimpleNamedCompilerPhase( name: String, description: String, + preactions: Set> = emptySet(), + postactions: Set> = emptySet(), outputIfNotEnabled: (PhaseConfigurationService, PhaserState, Context, Input) -> Output, - phaseBody: (Context, Input) -> Output -): SimpleNamedCompilerPhase = object : SimpleNamedCompilerPhase(name, description) { + op: (Context, Input) -> Output +): SimpleNamedCompilerPhase = object : SimpleNamedCompilerPhase( + name, + description, + preactions = preactions, + postactions = postactions.map { f -> + fun(actionState: ActionState, data: Pair, context: Context) = f(actionState, data.second, context) + }.toSet(), +) { override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input): Output = outputIfNotEnabled(phaseConfig, phaserState, context, input) override fun phaseBody(context: Context, input: Input): Output = - phaseBody(context, input) + op(context, input) } internal fun createSimpleNamedCompilerPhase( name: String, description: String, - phaseBody: (Context, Input) -> Unit -): SimpleNamedCompilerPhase = object : SimpleNamedCompilerPhase(name, description) { + preactions: Set> = emptySet(), + postactions: Set> = emptySet(), + op: (Context, Input) -> Unit +): SimpleNamedCompilerPhase = object : SimpleNamedCompilerPhase( + name, + description, + preactions = preactions, + postactions = postactions.map { f -> + fun(actionState: ActionState, data: Pair, context: Context) = f(actionState, data.first, context) + }.toSet(), +) { override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: Context, input: Input) {} override fun phaseBody(context: Context, input: Input): Unit = - phaseBody(context, input) + op(context, input) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PsiToIr.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PsiToIr.kt index 596df6f614c..ffe1f53e4a2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PsiToIr.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/PsiToIr.kt @@ -33,14 +33,23 @@ data class PsiToIrInput( val isProducingLibrary: Boolean, ) -// TODO: Possible improvement: a separate class for klib output (without irModules and irLinker). -internal class PsiToIrOutput( - val irModules: Map, +internal sealed class PsiToIrOutput( val irModule: IrModuleFragment, - val expectDescriptorToSymbol: MutableMap, val symbols: KonanSymbols, - val irLinker: KonanIrLinker?, -) +) { + class ForBackend( + val irModules: Map, + irModule: IrModuleFragment, + symbols: KonanSymbols, + val irLinker: KonanIrLinker, + ) : PsiToIrOutput(irModule, symbols) + + class ForKlib( + irModule: IrModuleFragment, + symbols: KonanSymbols, + val expectDescriptorToSymbol: MutableMap, + ): PsiToIrOutput(irModule, symbols) +} // TODO: Consider component-based approach internal interface PsiToIrContext : PhaseContext { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt index a5587bb035c..aa3cd4322b4 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Serializer.kt @@ -20,7 +20,7 @@ import org.jetbrains.kotlin.library.SerializedMetadata internal data class SerializerInput( val moduleDescriptor: ModuleDescriptor, - val psiToIrOutput: PsiToIrOutput?, + val psiToIrOutput: PsiToIrOutput.ForKlib?, ) data class SerializerOutput( @@ -58,13 +58,13 @@ internal val SerializerPhase = createSimpleNamedCompilerPhase PhaseEngine.runSerializer( moduleDescriptor: ModuleDescriptor, - psiToIrResult: PsiToIrOutput?, + psiToIrResult: PsiToIrOutput.ForKlib?, ): SerializerOutput { val input = SerializerInput(moduleDescriptor, psiToIrResult) return this.runPhase(SerializerPhase, input) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt index f62b322667c..ab916fd0f11 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt @@ -8,20 +8,20 @@ package org.jetbrains.kotlin.backend.konan.driver.phases import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine -import org.jetbrains.kotlin.backend.konan.driver.runPhaseInParentContext -import org.jetbrains.kotlin.backend.konan.llvm.linkBitcodeDependenciesPhase -import org.jetbrains.kotlin.backend.konan.llvm.printBitcodePhase -import org.jetbrains.kotlin.backend.konan.llvm.verifyBitcodePhase import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.ir.declarations.IrFile import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrDeclaration import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.ir.declarations.impl.IrFileImpl +import org.jetbrains.kotlin.ir.declarations.impl.IrModuleFragmentImpl import org.jetbrains.kotlin.ir.declarations.path import org.jetbrains.kotlin.ir.util.hasAnnotation +import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.target.CompilerOutputKind internal fun PhaseEngine.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? { - val frontendOutput = useContext(FrontendContextImpl(config)) { it.runFrontend(environment) } + val frontendOutput = useContext(FrontendContextImpl(config)) { it.runPhase(FrontendPhase, environment) } return frontendOutput as? FrontendPhaseOutput.Full } @@ -41,20 +41,23 @@ internal fun PhaseEngine.runPsiToIr( val additionalOutput = produceAdditionalOutput(psiToIrEngine) val psiToIrInput = PsiToIrInput(frontendOutput.moduleDescriptor, frontendOutput.environment, isProducingLibrary) val output = psiToIrEngine.runPhase(PsiToIrPhase, psiToIrInput) - psiToIrEngine.runSpecialBackendChecks(output) + psiToIrEngine.runSpecialBackendChecks(output.irModule, output.symbols) output to additionalOutput } runPhase(CopyDefaultValuesToActualPhase, psiToIrOutput.irModule) return psiToIrOutput to additionalOutput } -internal fun PhaseEngine.runBackend(backendContext: Context) { +internal fun PhaseEngine.runBackend(backendContext: Context, irModule: IrModuleFragment) { useContext(backendContext) { backendEngine -> backendEngine.runPhase(functionsWithoutBoundCheck) - backendEngine.processModuleFragments(backendEngine.context.irModule!!) { generationState, fragment -> + val fragments = backendEngine.splitIntoFragments(irModule) + fragments.forEach { (generationState, fragment) -> backendEngine.useContext(generationState) { generationStateEngine -> - // TODO: We can run compile part in parallel if we get rid of context.generationState. - generationStateEngine.runLowerAndCompile(fragment) + // TODO: Make this work if we first compile all the fragments and only after that run the link phases. + val it = generationStateEngine.compileModule(fragment) + // Split here + compileAndLink(it, it.outputFiles.mainFileName, it.outputFiles, it.temporaryFiles, isCoverageEnabled = false) } } } @@ -68,35 +71,26 @@ private fun isReferencedByNativeRuntime(declarations: List): Bool it is IrClass && isReferencedByNativeRuntime(it.declarations) } -internal fun PhaseEngine.processModuleFragments( +private fun PhaseEngine.splitIntoFragments( input: IrModuleFragment, - action: (NativeGenerationState, IrModuleFragment) -> Unit -): Unit = if (context.config.producePerFileCache) { - val module = context.irModules[context.config.libraryToCache!!.klib.libraryName] - ?: error("No module for the library being cached: ${context.config.libraryToCache!!.klib.libraryName}") - +): Sequence> = if (context.config.producePerFileCache) { + val module = input val files = module.files.toList() - module.files.clear() - val stdlibIsBeingCached = module.descriptor == context.stdlibModule val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached } ?.filter { it.isFunctionInterfaceFile }.orEmpty() val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached } ?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty() - for (file in files) { - if (file.isFunctionInterfaceFile) continue - + files.asSequence().filter { !it.isFunctionInterfaceFile }.map { file -> val generationState = NativeGenerationState( context.config, context, CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString()) ) - context.generationState = generationState - - module.files += file + val fragment = IrModuleFragmentImpl(input.descriptor, input.irBuiltins, listOf(file)) if (generationState.shouldDefineFunctionClasses) - module.files += functionInterfaceFiles + fragment.files += functionInterfaceFiles if (generationState.shouldLinkRuntimeNativeLibraries) { filesReferencedByNativeRuntime.forEach { @@ -104,56 +98,132 @@ internal fun PhaseEngine.processModuleFragments( } } - action(context.generationState, input) - - module.files.clear() - context.irModule!!.files.clear() // [dependenciesLowerPhase] puts all files to [context.irModule] for codegen. + fragment.files.filterIsInstance().forEach { + it.module = fragment + } + generationState to fragment } - - module.files += files } else { - context.generationState = NativeGenerationState(context.config, context, context.config.libraryToCache?.strategy) - action(context.generationState, input) + val nativeGenerationState = NativeGenerationState(context.config, context, context.config.libraryToCache?.strategy) + sequenceOf(nativeGenerationState to input) } +internal data class ModuleCompilationOutput( + val bitcodeFile: String, + val dependenciesTrackingResult: DependenciesTrackingResult, + // Passing tempFiles and output files through this file looks silly and incorrect. + // TODO: Refactor these classes and remove them from here. + val temporaryFiles: TempFiles, + val outputFiles: OutputFiles, +) + /** - * Performs all the hard work: * 1. Runs IR lowerings * 2. Runs LTO. * 3. Translates IR to LLVM IR. * 4. Optimizes it. * 5. Serializes it to a bitcode file. - * 6. Compiles bitcode to an object file. - * 7. Performs binary linkage. - * ... And stores additional cache info. - * - * TODO: Split into more granular phases with explicit inputs and outputs. */ -internal fun PhaseEngine.runLowerAndCompile(module: IrModuleFragment) { +internal fun PhaseEngine.compileModule(module: IrModuleFragment): ModuleCompilationOutput { if (context.config.produce.isCache) { - runPhaseInParentContext(buildAdditionalCacheInfoPhase) + runPhase(BuildAdditionalCacheInfoPhase, module) } if (context.config.produce == CompilerOutputKind.PROGRAM) { - runPhaseInParentContext(entryPointPhase, module) + runPhase(EntryPointPhase, module) } runBackendCodegen(module) + runBitcodePostProcessing() if (context.config.produce.isCache) { - runPhaseInParentContext(saveAdditionalCacheInfoPhase) + runPhase(SaveAdditionalCacheInfoPhase) } - runPhase(WriteBitcodeFilePhase) - runPhaseInParentContext(objectFilesPhase) - runPhaseInParentContext(linkerPhase) + val bitcodeFile = runPhase(WriteBitcodeFilePhase, context.llvm.module) + val dependenciesTrackingResult = DependenciesTrackingResult( + context.dependenciesTracker.bitcodeToLink, + context.dependenciesTracker.allNativeDependencies, + context.dependenciesTracker.allCachedBitcodeDependencies, + ) + return ModuleCompilationOutput(bitcodeFile, dependenciesTrackingResult, context.tempFiles, context.outputFiles) +} + +internal fun PhaseEngine.compileAndLink( + moduleCompilationOutput: ModuleCompilationOutput, + linkerOutputFile: String, + outputFiles: OutputFiles, + temporaryFiles: TempFiles, + isCoverageEnabled: Boolean, +) { + val objectFiles = runPhase(ObjectFilesPhase, ObjectFilesPhaseInput(moduleCompilationOutput.bitcodeFile, temporaryFiles)) + val linkerPhaseInput = LinkerPhaseInput(linkerOutputFile, objectFiles, moduleCompilationOutput.dependenciesTrackingResult, + outputFiles, temporaryFiles, isCoverageEnabled = isCoverageEnabled) + runPhase(LinkerPhase, linkerPhaseInput) if (context.config.produce.isCache) { - runPhaseInParentContext(finalizeCachePhase) + runPhase(FinalizeCachePhase, outputFiles) } } + internal fun PhaseEngine.runBackendCodegen(module: IrModuleFragment) { - runPhaseInParentContext(allLoweringsPhase, module) - runPhaseInParentContext(dependenciesLowerPhase, module) - runPhaseInParentContext(bitcodePhase, module) - runPhaseInParentContext(verifyBitcodePhase, module) - runPhaseInParentContext(printBitcodePhase, module) - runPhaseInParentContext(linkBitcodeDependenciesPhase, module) - runPhaseInParentContext(bitcodePostprocessingPhase, module) + runAllLowerings(module) + val dependenciesToCompile = findDependenciesToCompile() + // TODO: KonanLibraryResolver.TopologicalLibraryOrder actually returns libraries in the reverse topological order. + // TODO: Does the order of files really matter with the new MM? + dependenciesToCompile.reversed().forEach { irModule -> + runAllLowerings(irModule) + } + mergeDependencies(module, dependenciesToCompile) + runCodegen(module) + runPhase(CStubsPhase) + // TODO: Consider extracting llvmModule and friends from nativeGenerationState and pass them explicitly. + // Motivation: possibility to run LTO on bitcode level after separate IR compilation. + val llvmModule = context.llvm.module + if (context.config.needCompilerVerification || context.config.configuration.getBoolean(KonanConfigKeys.VERIFY_BITCODE)) { + runPhase(VerifyBitcodePhase, llvmModule) + } + if (context.shouldPrintBitCode()) { + runPhase(PrintBitcodePhase, llvmModule) + } + runPhase(LinkBitcodeDependenciesPhase) +} + +/** + * Compile lowered [module] to object file. + * @return absolute path to object file. + */ +private fun PhaseEngine.runCodegen(module: IrModuleFragment) { + val optimize = context.shouldOptimize() + module.files.forEach { + runPhase(ReturnsInsertionPhase, it) + } + val moduleDFG = runPhase(BuildDFGPhase, module, disable = !optimize) + val devirtualizationAnalysisResults = runPhase(DevirtualizationAnalysisPhase, DevirtualizationAnalysisInput(module, moduleDFG), disable = !optimize) + val dceResult = runPhase(DCEPhase, DCEInput(module, moduleDFG, devirtualizationAnalysisResults), disable = !optimize) + runPhase(RemoveRedundantCallsToStaticInitializersPhase, RedundantCallsInput(moduleDFG, devirtualizationAnalysisResults, module), disable = !optimize) + runPhase(DevirtualizationPhase, DevirtualizationInput(module, devirtualizationAnalysisResults), disable = !optimize) + // Have to run after link dependencies phase, because fields from dependencies can be changed during lowerings. + // Inline accessors only in optimized builds due to separate compilation and possibility to get broken debug information. + module.files.forEach { + runPhase(PropertyAccessorInlinePhase, it, disable = !optimize) + runPhase(InlineClassPropertyAccessorsPhase, it, disable = !optimize) + runPhase(RedundantCoercionsCleaningPhase, it) + // depends on redundantCoercionsCleaningPhase + runPhase(UnboxInlinePhase, it, disable = !optimize) + + } + runPhase(CreateLLVMDeclarationsPhase, module) + runPhase(GHAPhase, module, disable = !optimize) + runPhase(RTTIPhase, RTTIInput(module, dceResult)) + val lifetimes = runPhase(EscapeAnalysisPhase, EscapeAnalysisInput(module, moduleDFG, devirtualizationAnalysisResults), disable = !optimize) + runPhase(CodegenPhase, CodegenInput(module, lifetimes)) +} + +private fun PhaseEngine.findDependenciesToCompile(): List { + return context.config.librariesWithDependencies() + .mapNotNull { context.context.irModules[it.libraryName] } + .filter { context.llvmModuleSpecification.containsModule(it) } +} + +// Save all files for codegen in reverse topological order. +// This guarantees that libraries initializers are emitted in correct order. +private fun mergeDependencies(targetModule: IrModuleFragment, dependencies: List) { + targetModule.files.addAll(0, dependencies.flatMap { it.files }) } \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Utilities.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Utilities.kt new file mode 100644 index 00000000000..9da2e8539d0 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/Utilities.kt @@ -0,0 +1,64 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.driver.phases + +import org.jetbrains.kotlin.backend.common.IrValidator +import org.jetbrains.kotlin.backend.common.IrValidatorConfig +import org.jetbrains.kotlin.backend.common.checkDeclarationParents +import org.jetbrains.kotlin.backend.common.phaser.* +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext +import org.jetbrains.kotlin.backend.konan.reportCompilationWarning +import org.jetbrains.kotlin.ir.IrElement + +internal fun irValidationCallback(state: ActionState, element: IrElement, context: Context) { + if (!context.config.needVerifyIr) return + + val validatorConfig = IrValidatorConfig( + abortOnError = false, + ensureAllNodesAreDifferent = true, + checkTypes = true, + checkDescriptors = false + ) + try { + element.accept(IrValidator(context, validatorConfig), null) + element.checkDeclarationParents() + } catch (t: Throwable) { + // TODO: Add reference to source. + if (validatorConfig.abortOnError) + throw IllegalStateException("Failed IR validation ${state.beforeOrAfter} ${state.phase}", t) + else context.reportCompilationWarning("[IR VALIDATION] ${state.beforeOrAfter} ${state.phase}: ${t.message}") + } +} + +internal val nativeStateIrValidator = + fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) { + irValidationCallback(actionState, data, context.context) + } + +internal val nativeStateDumper = + fun(actionState: ActionState, data: IrElement, context: NativeGenerationState) { + defaultDumper(actionState, data, context.context) + } + +internal fun SimpleNamedCompilerPhase.copy( + preactions: Set> = emptySet(), + postactions: Set> = emptySet(), +) = object : SimpleNamedCompilerPhase( + name, + description, + preactions = preactions, + postactions = postactions.map { f -> + fun(actionState: ActionState, data: Pair, context: C) { f(actionState, data.second, context) } + }.toSet(), +) { + override fun outputIfNotEnabled(phaseConfig: PhaseConfigurationService, phaserState: PhaserState, context: C, input: Input): Output = + this@copy.outputIfNotEnabled(phaseConfig, phaserState, context, input) + + override fun phaseBody(context: C, input: Input): Output = + this@copy.phaseBody(context, input) +} \ No newline at end of file diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index 27a12f82383..0b4af279423 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -41,9 +41,7 @@ object KonanNameConventions { } // This is what Context collects about IR. -internal class KonanIr(context: Context): Ir(context) { - override var symbols: KonanSymbols by Delegates.notNull() -} +internal class KonanIr(context: Context, override val symbols: KonanSymbols): Ir(context) internal abstract class KonanSymbols( context: PhaseContext, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt deleted file mode 100644 index 19263f17c1f..00000000000 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/BitcodePhases.kt +++ /dev/null @@ -1,338 +0,0 @@ -/* - * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license - * that can be found in the LICENSE file. - */ - -package org.jetbrains.kotlin.backend.konan.llvm - -import llvm.* -import org.jetbrains.kotlin.backend.common.phaser.* -import org.jetbrains.kotlin.backend.konan.* -import org.jetbrains.kotlin.backend.konan.descriptors.GlobalHierarchyAnalysis -import org.jetbrains.kotlin.backend.konan.llvm.coverage.runCoveragePass -import org.jetbrains.kotlin.backend.konan.lower.InlineClassPropertyAccessorsLowering -import org.jetbrains.kotlin.backend.konan.lower.RedundantCoercionsCleaner -import org.jetbrains.kotlin.backend.konan.lower.ReturnsInsertionLowering -import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport -import org.jetbrains.kotlin.backend.konan.optimizations.* -import org.jetbrains.kotlin.ir.IrElement -import org.jetbrains.kotlin.ir.IrStatement -import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.ir.visitors.* -import org.jetbrains.kotlin.util.OperatorNameConventions - -internal val createLLVMDeclarationsPhase = makeKonanModuleOpPhase( - name = "CreateLLVMDeclarations", - description = "Map IR declarations to LLVM", - op = { context, _ -> context.generationState.llvmDeclarations = createLlvmDeclarations(context.generationState, context.irModule!!) } -) - -internal val RTTIPhase = makeKonanModuleOpPhase( - name = "RTTI", - description = "RTTI generation", - op = { context, irModule -> - val visitor = RTTIGeneratorVisitor(context.generationState) - irModule.acceptVoid(visitor) - visitor.dispose() - } -) - -internal val buildDFGPhase = makeKonanModuleOpPhase( - name = "BuildDFG", - description = "Data flow graph building", - op = { context, irModule -> - context.moduleDFG = ModuleDFGBuilder(context, irModule).build() - } -) - -internal val returnsInsertionPhase = makeKonanModuleOpPhase( - name = "ReturnsInsertion", - description = "Returns insertion for Unit functions", - //prerequisite = setOf(autoboxPhase, coroutinesPhase, enumClassPhase), TODO: if there are no files in the module, this requirement fails. - op = { context, irModule -> irModule.files.forEach { ReturnsInsertionLowering(context).lower(it) } } -) - -internal val inlineClassPropertyAccessorsPhase = makeKonanModuleOpPhase( - name = "InlineClassPropertyAccessorsLowering", - description = "Inline class property accessors", - op = { context, irModule -> irModule.files.forEach { InlineClassPropertyAccessorsLowering(context).lower(it) } } -) - -internal val devirtualizationAnalysisPhase = makeKonanModuleOpPhase( - name = "DevirtualizationAnalysis", - description = "Devirtualization analysis", - prerequisite = setOf(buildDFGPhase), - op = { context, _ -> - context.devirtualizationAnalysisResult = DevirtualizationAnalysis.run( - context, context.moduleDFG!!, ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) - ) - } -) - -internal val redundantCoercionsCleaningPhase = makeKonanModuleOpPhase( - name = "RedundantCoercionsCleaning", - description = "Redundant coercions cleaning", - op = { context, irModule -> irModule.files.forEach { RedundantCoercionsCleaner(context).lower(it) } } -) - -internal val ghaPhase = makeKonanModuleOpPhase( - name = "GHAPhase", - description = "Global hierarchy analysis", - op = { context, irModule -> GlobalHierarchyAnalysis(context, irModule).run() } -) - -internal val IrFunction.longName: String - get() = "${(parent as? IrClass)?.name?.asString() ?: ""}.${(this as? IrSimpleFunction)?.name ?: ""}" - -internal val dcePhase = makeKonanModuleOpPhase( - name = "DCEPhase", - description = "Dead code elimination", - prerequisite = setOf(devirtualizationAnalysisPhase), - op = { context, _ -> - val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) - - val callGraph = CallGraphBuilder( - context, context.moduleDFG!!, - externalModulesDFG, - context.devirtualizationAnalysisResult!!, - // For DCE we don't wanna miss any potentially reachable function. - nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE - ).build() - - val referencedFunctions = mutableSetOf() - callGraph.rootExternalFunctions.forEach { - if (!it.isStaticFieldInitializer) - referencedFunctions.add(it.irFunction ?: error("No IR for: $it")) - } - for (node in callGraph.directEdges.values) { - if (!node.symbol.isStaticFieldInitializer) - referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}")) - node.callSites.forEach { - assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" } - referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}")) - } - } - - context.irModule!!.acceptChildrenVoid(object: IrElementVisitorVoid { - override fun visitElement(element: IrElement) { - element.acceptChildrenVoid(this) - } - - override fun visitFunction(declaration: IrFunction) { - // TODO: Generalize somehow, not that graceful. - if (declaration.name == OperatorNameConventions.INVOKE - && declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) { - referencedFunctions.add(declaration) - } - super.visitFunction(declaration) - } - - override fun visitConstructor(declaration: IrConstructor) { - // TODO: NativePointed is the only inline class for which the field's type and - // the constructor parameter's type are different. - // Thus we need to conserve the constructor no matter if it was actually referenced somehow or not. - // See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why. - if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary) - referencedFunctions.add(declaration) - super.visitConstructor(declaration) - } - }) - - context.irModule!!.transformChildrenVoid(object: IrElementTransformerVoid() { - override fun visitFile(declaration: IrFile): IrFile { - declaration.declarations.removeAll { - (it is IrFunction && !referencedFunctions.contains(it)) - } - return super.visitFile(declaration) - } - - override fun visitClass(declaration: IrClass): IrStatement { - if (declaration == context.ir.symbols.nativePointed) - return super.visitClass(declaration) - declaration.declarations.removeAll { - (it is IrFunction && it.isReal && !referencedFunctions.contains(it)) - } - return super.visitClass(declaration) - } - - override fun visitProperty(declaration: IrProperty): IrStatement { - if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) { - declaration.getter = null - } - if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) { - declaration.setter = null - } - return super.visitProperty(declaration) - } - }) - - context.referencedFunctions = referencedFunctions - } -) - -internal val removeRedundantCallsToStaticInitializersPhase = makeKonanModuleOpPhase( - name = "RemoveRedundantCallsToStaticInitializersPhase", - description = "Redundant static initializers calls removal", - prerequisite = setOf(devirtualizationAnalysisPhase), - op = { context, _ -> - val moduleDFG = context.moduleDFG!! - val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) - - val callGraph = CallGraphBuilder( - context, moduleDFG, - externalModulesDFG, - context.devirtualizationAnalysisResult!!, - nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE - ).build() - - val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG) - .mapNotNull { it.irFunction } - .toSet() - - StaticInitializersOptimization.removeRedundantCalls(context, callGraph, rootSet) - } -) - -internal val devirtualizationPhase = makeKonanModuleOpPhase( - name = "Devirtualization", - description = "Devirtualization", - prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase), - op = { context, irModule -> - val devirtualizedCallSites = - context.devirtualizationAnalysisResult!!.devirtualizedCallSites - .asSequence() - .filter { it.key.irCallSite != null } - .associate { it.key.irCallSite!! to it.value } - DevirtualizationAnalysis.devirtualize(irModule, context, - ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()), devirtualizedCallSites) - } -) - -internal val escapeAnalysisPhase = makeKonanModuleOpPhase( - name = "EscapeAnalysis", - description = "Escape analysis", - prerequisite = setOf(buildDFGPhase, devirtualizationAnalysisPhase), - op = { context, _ -> - val entryPoint = context.ir.symbols.entryPoint?.owner - val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) - val nonDevirtualizedCallSitesUnfoldFactor = - if (entryPoint != null) { - // For a final program it can be safely assumed that what classes we see is what we got, - // so can take those. In theory we can always unfold call sites using type hierarchy, but - // the analysis might converge much, much slower, so take only reasonably small for now. - 5 - } - else { - // Can't tolerate any non-devirtualized call site for a library. - // TODO: What about private virtual functions? - // Note: 0 is also bad - this means that there're no inheritors in the current source set, - // but there might be some provided by the users of the library being produced. - -1 - } - val callGraph = CallGraphBuilder( - context, context.moduleDFG!!, - externalModulesDFG, - context.devirtualizationAnalysisResult!!, - nonDevirtualizedCallSitesUnfoldFactor - ).build() - EscapeAnalysis.computeLifetimes( - context, context.moduleDFG!!, externalModulesDFG, callGraph, context.lifetimes - ) - } -) - -internal val codegenPhase = makeKonanModuleOpPhase( - name = "Codegen", - description = "Code generation", - op = { context, irModule -> - context.generationState.objCExport = ObjCExport( - context.generationState, - context.moduleDescriptor, - context.objCExportedInterface, - context.objCExportCodeSpec - ) - - irModule.acceptVoid(CodeGeneratorVisitor(context.generationState, context.lifetimes)) - - if (context.generationState.hasDebugInfo()) - DIFinalize(context.generationState.debugInfo.builder) - } -) - -internal val cStubsPhase = makeKonanModuleOpPhase( - name = "CStubs", - description = "C stubs compilation", - op = { context, _ -> produceCStubs(context.generationState) } -) - -internal val linkBitcodeDependenciesPhase = makeKonanModuleOpPhase( - name = "LinkBitcodeDependencies", - description = "Link bitcode dependencies", - op = { context, _ -> linkBitcodeDependencies(context.generationState) } -) - -internal val checkExternalCallsPhase = makeKonanModuleOpPhase( - name = "CheckExternalCalls", - description = "Check external calls", - op = { context, _ -> checkLlvmModuleExternalCalls(context.generationState) } -) - -internal val rewriteExternalCallsCheckerGlobals = makeKonanModuleOpPhase( - name = "RewriteExternalCallsCheckerGlobals", - description = "Rewrite globals for external calls checker after optimizer run", - op = { context, _ -> addFunctionsListSymbolForChecker(context.generationState) } -) - - - -internal val bitcodeOptimizationPhase = makeKonanModuleOpPhase( - name = "BitcodeOptimization", - description = "Optimize bitcode", - op = { context, _ -> - val generationState = context.generationState - val config = createLTOFinalPipelineConfig(context.generationState) - LlvmOptimizationPipeline(config, context.generationState.llvm.module, generationState).use { - it.run() - } - } -) - -internal val coveragePhase = makeKonanModuleOpPhase( - name = "Coverage", - description = "Produce coverage information", - op = { context, _ -> runCoveragePass(context.generationState) } -) - -internal val optimizeTLSDataLoadsPhase = makeKonanModuleOpPhase( - name = "OptimizeTLSDataLoads", - description = "Optimize multiple loads of thread data", - op = { context, _ -> removeMultipleThreadDataLoads(context.generationState) } -) - -internal val removeRedundantSafepointsPhase = makeKonanModuleOpPhase( - name = "RemoveRedundantSafepoints", - description = "Remove function prologue safepoints inlined to another function", - op = { context, _ -> - RemoveRedundantSafepointsPass().runOnModule( - module = context.generationState.llvm.module, - isSafepointInliningAllowed = context.shouldInlineSafepoints() - ) - } -) - -internal val verifyBitcodePhase = makeKonanModuleOpPhase( - name = "VerifyBitcode", - description = "Verify bitcode", - op = { context, _ -> context.verifyBitCode() } -) - -internal val printBitcodePhase = makeKonanModuleOpPhase( - name = "PrintBitcode", - description = "Print bitcode", - op = { context, _ -> - if (context.shouldPrintBitCode()) { - context.printBitCode() - } - } -) \ No newline at end of file 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 b472fe5d5ef..70253e3b6a7 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 @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.common.lower.inline.InlinerExpressionLocatio import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.cexport.CAdapterApiExporter import org.jetbrains.kotlin.backend.konan.cexport.CAdapterCodegen +import org.jetbrains.kotlin.backend.konan.cexport.CAdapterExportedElements import org.jetbrains.kotlin.backend.konan.cgen.CBridgeOrigin import org.jetbrains.kotlin.backend.konan.descriptors.* import org.jetbrains.kotlin.backend.konan.ir.* @@ -22,6 +23,8 @@ import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_STANDA import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.descriptors.PackageFragmentDescriptor +import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET @@ -83,8 +86,8 @@ internal fun IrField.isGlobalNonPrimitive(context: Context) = when { internal fun IrField.shouldBeFrozen(context: Context): Boolean = this.storageKind(context) == FieldStorageKind.SHARED_FROZEN -internal class RTTIGeneratorVisitor(generationState: NativeGenerationState) : IrElementVisitorVoid { - val generator = RTTIGenerator(generationState) +internal class RTTIGeneratorVisitor(generationState: NativeGenerationState, referencedFunctions: Set?) : IrElementVisitorVoid { + val generator = RTTIGenerator(generationState, referencedFunctions) val kotlinObjCClassInfoGenerator = KotlinObjCClassInfoGenerator(generationState) @@ -201,7 +204,11 @@ private interface CodeContext { //-------------------------------------------------------------------------// -internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, val lifetimes: Map) : IrElementVisitorVoid { +internal class CodeGeneratorVisitor( + val generationState: NativeGenerationState, + val irBuiltins: IrBuiltIns, + val lifetimes: Map +) : IrElementVisitorVoid { private val context = generationState.context private val llvm = generationState.llvm private val debugInfo: DebugInfo @@ -338,8 +345,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, return block() } } - private fun appendCAdapters() { - val elements = context.cAdapterExportedElements + private fun appendCAdapters(elements: CAdapterExportedElements) { CAdapterCodegen(codegen, generationState).buildAllAdaptersRecursively(elements) // TODO: It is not a part of IrToBitcode. Maybe move it somewhere? CAdapterApiExporter(generationState, elements).makeGlobalStruct() @@ -442,7 +448,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, appendLlvmUsed("llvm.used", llvm.usedFunctions + llvm.usedGlobals) appendLlvmUsed("llvm.compiler.used", llvm.compilerUsedGlobals) if (context.config.produce.isNativeLibrary) { - appendCAdapters() + context.cAdapterExportedElements?.let { appendCAdapters(it) } } } @@ -899,12 +905,17 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, declaration.backingField?.acceptVoid(this) } - //-------------------------------------------------------------------------// + private fun needGlobalInit(field: IrField): Boolean { + if (field.descriptor.containingDeclaration !is PackageFragmentDescriptor) return field.isStatic + // TODO: add some smartness here. Maybe if package of the field is in never accessed + // assume its global init can be actually omitted. + return true + } override fun visitField(declaration: IrField) { context.log{"visitField : ${ir2string(declaration)}"} debugFieldDeclaration(declaration) - if (context.needGlobalInit(declaration)) { + if (needGlobalInit(declaration)) { val type = declaration.type.toLLVMType(llvm) val globalPropertyAccess = generationState.llvmDeclarations.forStaticField(declaration).storageAddressAccess val initializer = declaration.initializer?.expression @@ -1684,7 +1695,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, when { !value.symbol.owner.isStatic -> { fieldAddress = fieldPtrOfClass(evaluateExpression(value.receiver!!), value.symbol.owner) - alignment = context.generationState.llvmDeclarations.forField(value.symbol.owner).alignment + alignment = generationState.llvmDeclarations.forField(value.symbol.owner).alignment } value.symbol.owner.correspondingPropertySymbol?.owner?.isConst == true -> { // TODO: probably can be removed, as they are inlined. @@ -1762,7 +1773,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, functionGenerationContext.call(llvm.checkLifetimesConstraint, listOf(thisPtr, valueToAssign)) } address = fieldPtrOfClass(thisPtr, value.symbol.owner) - alignment = context.generationState.llvmDeclarations.forField(value.symbol.owner).alignment + alignment = generationState.llvmDeclarations.forField(value.symbol.owner).alignment } else { assert(value.receiver == null) if (context.config.threadsAreAllowed && value.symbol.owner.storageKind(context) == FieldStorageKind.GLOBAL) @@ -2537,7 +2548,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState, private fun evaluateOperatorCall(callee: IrCall, args: List): LLVMValueRef { context.log{"evaluateOperatorCall : origin:${ir2string(callee)}"} val function = callee.symbol.owner - val ib = context.irModule!!.irBuiltins + val ib = context.irBuiltIns with(functionGenerationContext) { val functionSymbol = function.symbol diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt index 6ab58ceaa3e..2ef1c529634 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmDeclarations.kt @@ -339,7 +339,7 @@ private class DeclarationsGeneratorVisitor(override val generationState: NativeG } else { // Fields are module-private, so we use internal name: val name = "kvar:" + qualifyInternalName(declaration) - val alignmnet = declaration.requiredAlignment(context) + val alignmnet = declaration.requiredAlignment(llvm) val storage = if (declaration.storageKind(context) == FieldStorageKind.THREAD_LOCAL) { addKotlinThreadLocal(name, declaration.type.toLLVMType(llvm), alignmnet) } else { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt index 517749863d0..c5025c717c7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/RTTIGenerator.kt @@ -17,7 +17,10 @@ import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* -internal class RTTIGenerator(override val generationState: NativeGenerationState) : ContextUtils { +internal class RTTIGenerator( + override val generationState: NativeGenerationState, + private val referencedFunctions: Set?, +) : ContextUtils { private val acyclicCache = mutableMapOf() private val safeAcyclicFieldTypes = setOf( @@ -51,7 +54,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState private fun flagsFromClass(irClass: IrClass): Int { var result = 0 if (irClass.isFrozen(context)) - result = result or TF_IMMUTABLE + result = result or TF_IMMUTABLE // TODO: maybe perform deeper analysis to find surely acyclic types. if (!irClass.isInterface && !irClass.isAbstract() && !irClass.isAnnotationClass) { if (checkAcyclicClass(irClass)) { @@ -294,7 +297,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState // TODO: compile-time resolution limits binary compatibility. val vtableEntries = context.getLayoutBuilder(irClass).vtableEntries.map { val implementation = it.implementation - if (implementation == null || implementation.isExternalObjCClassMethod() || context.referencedFunctions?.contains(implementation) == false) { + if (implementation == null || implementation.isExternalObjCClassMethod() || referencedFunctions?.contains(implementation) == false) { NullPointer(llvm.int8Type) } else { implementation.entryPointAddress @@ -375,7 +378,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState else { val vtableEntries = iface.interfaceVTableEntries.map { ifaceFunction -> val impl = layoutBuilder.overridingOf(ifaceFunction) - if (impl == null || context.referencedFunctions?.contains(impl) == false) + if (impl == null || referencedFunctions?.contains(impl) == false) NullPointer(llvm.int8Type) else impl.entryPointAddress } @@ -567,7 +570,7 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState associatedObjects = null, processObjectInMark = genProcessObjectInMark(bodyType), requiredAlignment = runtime.objectAlignment - ), vtable) + ), vtable) typeInfoWithVtableGlobal.setInitializer(typeInfoWithVtable) typeInfoWithVtableGlobal.setConstant(true) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt index 24283c52253..6c20775f53d 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt @@ -62,11 +62,14 @@ internal class CoverageManager(val generationState: NativeGenerationState) { filesRegionsInfo.flatMap { it.functions }.firstOrNull { it.function == irFunction } private val coveredModules: Set by lazy { - val coveredUserCode = if (shouldCoverSources) setOf(context.moduleDescriptor) else emptySet() + val coveredSources = if (shouldCoverSources) { + context.sourcesModules + } else { + emptySet() + } val coveredLibs = context.irModules.filter { it.key in librariesToCover }.values .map { it.descriptor }.toSet() - val coveredIncludedLibs = if (shouldCoverSources) context.getIncludedLibraryDescriptors().toSet() else emptySet() - coveredLibs + coveredUserCode + coveredIncludedLibs + coveredLibs + coveredSources } private fun fileCoverageFilter(file: IrFile) = diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index e75a49fedf1..a34c295eff5 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -218,7 +218,8 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo val runtime get() = codegen.runtime val staticData get() = codegen.staticData - val rttiGenerator = RTTIGenerator(generationState) + // TODO: pass referenced functions explicitly + val rttiGenerator = RTTIGenerator(generationState, referencedFunctions = null) fun dispose() { rttiGenerator.dispose() diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt index 1f32ea23f03..0a0bd6c79be 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt @@ -32,7 +32,7 @@ import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid */ internal class BuiltinOperatorLowering(val context: Context) : FileLoweringPass, IrBuildingTransformer(context) { - private val irBuiltins = context.irModule!!.irBuiltins + private val irBuiltins = context.irBuiltIns private val symbols = context.ir.symbols override fun lower(irFile: IrFile) { 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 2632b4e5ae6..8b161b22a57 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 @@ -37,7 +37,7 @@ internal class CacheInfoBuilder( if (!declaration.isInterface && !declaration.isLocal && declaration.isExported && declaration.origin != DECLARATION_ORIGIN_FUNCTION_CLASS ) { - val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields() + val declaredFields = generationState.context.getLayoutBuilder(declaration).getDeclaredFields(generationState.llvm) generationState.classFields.add(moduleDeserializer.buildClassFields(declaration, declaredFields)) } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DataClassOperatorsLowering.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DataClassOperatorsLowering.kt index 10ea62f24a2..599ec5c1469 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DataClassOperatorsLowering.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/DataClassOperatorsLowering.kt @@ -21,7 +21,7 @@ import org.jetbrains.kotlin.ir.util.irCall import org.jetbrains.kotlin.ir.visitors.IrElementTransformer internal class DataClassOperatorsLowering(val context: Context) : FileLoweringPass, IrElementTransformer { - private val irBuiltins = context.irModule!!.irBuiltins + private val irBuiltins = context.irBuiltIns override fun lower(irFile: IrFile) { irFile.transformChildren(this, null) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TestProcessor.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TestProcessor.kt index ba1b8013548..1a184a15987 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TestProcessor.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/TestProcessor.kt @@ -680,7 +680,7 @@ internal class TestProcessor (val context: Context) { private fun shouldProcessFile(irFile: IrFile): Boolean = irFile.packageFragmentDescriptor.module.let { // Process test annotations in source libraries too. - it == context.moduleDescriptor || it in context.getIncludedLibraryDescriptors() + it in context.sourcesModules } fun process(irFile: IrFile) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt index 283522d487f..98214277113 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/CallGraphBuilder.kt @@ -10,6 +10,7 @@ import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.konan.DirectedGraph import org.jetbrains.kotlin.backend.konan.DirectedGraphNode import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment internal class CallGraphNode(val graph: CallGraph, val symbol: DataFlowIR.FunctionSymbol.Declared) : DirectedGraphNode { @@ -53,6 +54,7 @@ internal class CallGraph(val directEdges: Map() fun build(): CallGraph { - val rootSet = DevirtualizationAnalysis.computeRootSet(context, moduleDFG, externalModulesDFG) + val rootSet = DevirtualizationAnalysis.computeRootSet(context, irModule, moduleDFG, externalModulesDFG) for (symbol in rootSet) { val function = moduleDFG.functions[symbol] if (function == null) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DCE.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DCE.kt new file mode 100644 index 00000000000..4f9d0cfa7c6 --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DCE.kt @@ -0,0 +1,110 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.konan.optimizations + +import org.jetbrains.kotlin.backend.konan.Context +import org.jetbrains.kotlin.backend.konan.InteropFqNames +import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.util.defaultType +import org.jetbrains.kotlin.ir.util.isFunction +import org.jetbrains.kotlin.ir.util.isReal +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid +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.util.OperatorNameConventions + +internal fun dce( + context: Context, + irModule: IrModuleFragment, + moduleDFG: ModuleDFG, + devirtualizationAnalysisResult: DevirtualizationAnalysis.AnalysisResult, +): Set { + val externalModulesDFG = ExternalModulesDFG(emptyList(), emptyMap(), emptyMap(), emptyMap()) + + val callGraph = CallGraphBuilder( + context, + irModule, + moduleDFG, + externalModulesDFG, + devirtualizationAnalysisResult, + // For DCE we don't wanna miss any potentially reachable function. + nonDevirtualizedCallSitesUnfoldFactor = Int.MAX_VALUE + ).build() + + val referencedFunctions = mutableSetOf() + callGraph.rootExternalFunctions.forEach { + if (!it.isStaticFieldInitializer) + referencedFunctions.add(it.irFunction ?: error("No IR for: $it")) + } + for (node in callGraph.directEdges.values) { + if (!node.symbol.isStaticFieldInitializer) + referencedFunctions.add(node.symbol.irFunction ?: error("No IR for: ${node.symbol}")) + node.callSites.forEach { + assert (!it.isVirtual) { "There should be no virtual calls in the call graph, but was: ${it.actualCallee}" } + referencedFunctions.add(it.actualCallee.irFunction ?: error("No IR for: ${it.actualCallee}")) + } + } + + irModule.acceptChildrenVoid(object: IrElementVisitorVoid { + override fun visitElement(element: IrElement) { + element.acceptChildrenVoid(this) + } + + override fun visitFunction(declaration: IrFunction) { + // TODO: Generalize somehow, not that graceful. + if (declaration.name == OperatorNameConventions.INVOKE + && declaration.parent.let { it is IrClass && it.defaultType.isFunction() }) { + referencedFunctions.add(declaration) + } + super.visitFunction(declaration) + } + + override fun visitConstructor(declaration: IrConstructor) { + // TODO: NativePointed is the only inline class for which the field's type and + // the constructor parameter's type are different. + // Thus we need to conserve the constructor no matter if it was actually referenced somehow or not. + // See [IrTypeInlineClassesSupport.getInlinedClassUnderlyingType] why. + if (declaration.parentAsClass.name.asString() == InteropFqNames.nativePointedName && declaration.isPrimary) + referencedFunctions.add(declaration) + super.visitConstructor(declaration) + } + }) + + irModule.transformChildrenVoid(object: IrElementTransformerVoid() { + override fun visitFile(declaration: IrFile): IrFile { + declaration.declarations.removeAll { + (it is IrFunction && !referencedFunctions.contains(it)) + } + return super.visitFile(declaration) + } + + override fun visitClass(declaration: IrClass): IrStatement { + if (declaration == context.ir.symbols.nativePointed) + return super.visitClass(declaration) + declaration.declarations.removeAll { + (it is IrFunction && it.isReal && !referencedFunctions.contains(it)) + } + return super.visitClass(declaration) + } + + override fun visitProperty(declaration: IrProperty): IrStatement { + if (declaration.getter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) { + declaration.getter = null + } + if (declaration.setter.let { it != null && it.isReal && !referencedFunctions.contains(it) }) { + declaration.setter = null + } + return super.visitProperty(declaration) + } + }) + + return referencedFunctions +} + diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt index b5b0c59ee35..120ac8589ff 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DFGBuilder.kt @@ -161,7 +161,7 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null private val module = DataFlowIR.Module(irModule.descriptor) - private val symbolTable = DataFlowIR.SymbolTable(context, irModule, module) + private val symbolTable = DataFlowIR.SymbolTable(context, module) // Possible values of a returnable block. private val returnableBlockValues = mutableMapOf>() @@ -172,6 +172,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag private val expressionValuesExtractor = ExpressionValuesExtractor(context, returnableBlockValues, suspendableExpressionValues) fun build(): ModuleDFG { + symbolTable.populateWith(irModule) + val functions = mutableMapOf() irModule.accept(object : IrElementVisitorVoid { @@ -548,8 +550,8 @@ internal class ModuleDFGBuilder(val context: Context, val irModule: IrModuleFrag values = mutableListOf(), type = symbolTable.mapType(irVariable.type), kind = if (catchParameters.contains(irVariable)) - DataFlowIR.VariableKind.CatchParameter - else DataFlowIR.VariableKind.Ordinary + DataFlowIR.VariableKind.CatchParameter + else DataFlowIR.VariableKind.Ordinary ) scope.nodes += node variables[irVariable] = Scoped(node, scope) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt index dfa770e86b8..55c4b9b3ae2 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DataFlowIR.kt @@ -240,7 +240,7 @@ internal object DataFlowIR { : Call(constructor, arguments, constructedType, irCallSite) open class VirtualCall(callee: FunctionSymbol, arguments: List, - val receiverType: Type, returnType: Type, override val irCallSite: IrCall?) + val receiverType: Type, returnType: Type, override val irCallSite: IrCall?) : Call(callee, arguments, returnType, irCallSite) class VtableCall(callee: FunctionSymbol, receiverType: Type, val calleeVtableIndex: Int, @@ -430,7 +430,7 @@ internal object DataFlowIR { } } - class SymbolTable(val context: Context, val irModule: IrModuleFragment, val module: Module) { + class SymbolTable(val context: Context, val module: Module) { private val TAKE_NAMES = true // Take fqNames for all functions and types (for debug purposes). @@ -454,7 +454,7 @@ internal object DataFlowIR { var privateTypeIndex = 1 // 0 for [Virtual] var privateFunIndex = 0 - init { + fun populateWith(irModule: IrModuleFragment) { irModule.accept(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) @@ -492,11 +492,11 @@ internal object DataFlowIR { val placeToClassTable = true val symbolTableIndex = if (placeToClassTable) module.numberOfClasses++ else -1 val type = if (irClass.isExported()) - Type.Public(localHash(name.toByteArray()), privateTypeIndex++, isFinal, isAbstract, null, - module, symbolTableIndex, irClass, takeName { name }) - else - Type.Private(privateTypeIndex++, isFinal, isAbstract, null, - module, symbolTableIndex, irClass, takeName { name }) + Type.Public(localHash(name.toByteArray()), privateTypeIndex++, isFinal, isAbstract, null, + module, symbolTableIndex, irClass, takeName { name }) + else + Type.Private(privateTypeIndex++, isFinal, isAbstract, null, + module, symbolTableIndex, irClass, takeName { name }) classMap[irClass] = type @@ -642,8 +642,8 @@ internal object DataFlowIR { functionMap[it] = symbol symbol.parameters = function.allParameters.map { it.type } - .map { mapTypeToFunctionParameter(it) } - .toTypedArray() + .map { mapTypeToFunctionParameter(it) } + .toTypedArray() symbol.returnParameter = mapTypeToFunctionParameter(function.returnType) return symbol diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt index 1a2e7a10d2b..3331976ad02 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/DevirtualizationAnalysis.kt @@ -55,7 +55,7 @@ internal object DevirtualizationAnalysis { private inline fun takeName(block: () -> String) = if (TAKE_NAMES) block() else null - fun computeRootSet(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) + fun computeRootSet(context: Context, irModule: IrModuleFragment, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) : List { fun DataFlowIR.FunctionSymbol.resolved(): DataFlowIR.FunctionSymbol { @@ -97,7 +97,7 @@ internal object DevirtualizationAnalysis { // At this point all function references are lowered except those leaking to the native world. // Conservatively assume them belonging of the root set. val leakingThroughFunctionReferences = mutableListOf() - context.irModule!!.acceptChildrenVoid(object : IrElementVisitorVoid { + irModule.acceptChildrenVoid(object : IrElementVisitorVoid { override fun visitElement(element: IrElement) { element.acceptChildrenVoid(this) } @@ -128,6 +128,7 @@ internal object DevirtualizationAnalysis { private val VIRTUAL_TYPE_ID = 0 // Id of [DataFlowIR.Type.Virtual]. internal class DevirtualizationAnalysisImpl(val context: Context, + val irModule: IrModuleFragment, val moduleDFG: ModuleDFG, val externalModulesDFG: ExternalModulesDFG) { @@ -234,7 +235,21 @@ internal object DevirtualizationAnalysis { private fun IntArray.getEdge(v: Int, id: Int) = this[this[v] + id] private fun IntArray.edgeCount(v: Int) = this[v + 1] - this[v] - inner class TypeHierarchy(val allTypes: Array) { + interface TypeHierarchy { + val allTypes: Array + + fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet + } + + object EmptyTypeHierarchy : TypeHierarchy { + override val allTypes: Array = emptyArray() + + override fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet { + return BitSet() + } + } + + inner class TypeHierarchyImpl(override val allTypes: Array) : TypeHierarchy { private val typesSubTypes = Array(allTypes.size) { mutableListOf() } private val allInheritors = Array(allTypes.size) { BitSet() } @@ -256,7 +271,7 @@ internal object DevirtualizationAnalysis { allTypes.forEach { processType(it) } } - fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet { + override fun inheritorsOf(type: DataFlowIR.Type.Declared): BitSet { val typeId = type.index val inheritors = allInheritors[typeId] if (!inheritors.isEmpty || type == DataFlowIR.Type.Virtual) return inheritors @@ -432,8 +447,8 @@ internal object DevirtualizationAnalysis { val allTypes = Array(allDeclaredTypes.size) { DataFlowIR.Type.Virtual } for (type in allDeclaredTypes) allTypes[type.index] = type - val typeHierarchy = TypeHierarchy(allTypes) - val rootSet = computeRootSet(context, moduleDFG, externalModulesDFG) + val typeHierarchy = TypeHierarchyImpl(allTypes) + val rootSet = computeRootSet(context, irModule, moduleDFG, externalModulesDFG) val nodesMap = mutableMapOf() @@ -695,7 +710,7 @@ internal object DevirtualizationAnalysis { // and by that we're only holding references to two out of three of them. private fun buildConstraintGraph(nodesMap: MutableMap, functions: Map, - typeHierarchy: TypeHierarchy, + typeHierarchy: TypeHierarchyImpl, rootSet: List ): ConstraintGraphBuildResult { val precursor = buildConstraintGraphPrecursor(nodesMap, functions, typeHierarchy, rootSet) @@ -728,7 +743,7 @@ internal object DevirtualizationAnalysis { private fun buildConstraintGraphPrecursor(nodesMap: MutableMap, functions: Map, - typeHierarchy: TypeHierarchy, + typeHierarchy: TypeHierarchyImpl, rootSet: List ): ConstraintGraphPrecursor { val constraintGraphBuilder = ConstraintGraphBuilder(nodesMap, functions, typeHierarchy, rootSet, true) @@ -765,7 +780,7 @@ internal object DevirtualizationAnalysis { private inner class ConstraintGraphBuilder(val functionNodesMap: MutableMap, val functions: Map, - val typeHierarchy: TypeHierarchy, + val typeHierarchy: TypeHierarchyImpl, val rootSet: List, val useTypes: Boolean) { @@ -962,9 +977,9 @@ internal object DevirtualizationAnalysis { symbol.parameters.forEachIndexed { index, type -> val resolvedType = type.type.resolved() val node = if (!resolvedType.isFinal) - constraintGraph.virtualNode // TODO: OBJC-INTEROP-GENERATED-CLASSES - else - concreteClass(resolvedType) + constraintGraph.virtualNode // TODO: OBJC-INTEROP-GENERATED-CLASSES + else + concreteClass(resolvedType) addEdge(node, parameters[index]) } } @@ -1308,8 +1323,8 @@ internal object DevirtualizationAnalysis { class AnalysisResult(val devirtualizedCallSites: Map, val typeHierarchy: DevirtualizationAnalysisImpl.TypeHierarchy) - fun run(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) = - DevirtualizationAnalysisImpl(context, moduleDFG, externalModulesDFG).analyze() + fun run(context: Context, irModule: IrModuleFragment, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG) = + DevirtualizationAnalysisImpl(context, irModule, moduleDFG, externalModulesDFG).analyze() fun devirtualize(irModule: IrModuleFragment, context: Context, externalModulesDFG: ExternalModulesDFG, devirtualizedCallSites: Map) { @@ -1331,8 +1346,8 @@ internal object DevirtualizationAnalysis { fun IrStatementsBuilder.irTemporary(value: IrExpression, tempName: String, type: IrType): IrVariable { val temporary = IrVariableImpl( - value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(), - Name.identifier(tempName), type, isVar = false, isConst = false, isLateinit = false + value.startOffset, value.endOffset, IrDeclarationOrigin.IR_TEMPORARY_VARIABLE, IrVariableSymbolImpl(), + Name.identifier(tempName), type, isVar = false, isConst = false, isLateinit = false ).apply { this.initializer = value } @@ -1361,7 +1376,7 @@ internal object DevirtualizationAnalysis { if (actualType.boxFunction == null && targetType.boxFunction == null) return null if (actualType.boxFunction != null && targetType.boxFunction != null) { assert (actualType.type.resolved() == targetType.type.resolved()) - { "Inconsistent types: ${actualType.type} and ${targetType.type}" } + { "Inconsistent types: ${actualType.type} and ${targetType.type}" } return null } if (actualType.boxFunction == null) diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt index c8bad568c0f..14314d87202 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/EscapeAnalysis.kt @@ -470,6 +470,7 @@ internal object EscapeAnalysis { private class InterproceduralAnalysis( val context: Context, + val generationState: NativeGenerationState, val callGraph: CallGraph, val intraproceduralAnalysisResults: Map, val externalModulesDFG: ExternalModulesDFG, @@ -729,7 +730,7 @@ internal object EscapeAnalysis { ?: (node as? DataFlowIR.Node.Variable) ?.values?.singleOrNull()?.let { arrayLengthOf(it.node) } - private val pointerSize = context.generationState.runtime.pointerSize + private val pointerSize = generationState.runtime.pointerSize private fun arrayItemSizeOf(irClass: IrClass): Int? = when (irClass.symbol) { symbols.array -> pointerSize @@ -1796,14 +1797,20 @@ internal object EscapeAnalysis { } } - fun computeLifetimes(context: Context, moduleDFG: ModuleDFG, externalModulesDFG: ExternalModulesDFG, - callGraph: CallGraph, lifetimes: MutableMap) { + fun computeLifetimes( + context: Context, + generationState: NativeGenerationState, + moduleDFG: ModuleDFG, + externalModulesDFG: ExternalModulesDFG, + callGraph: CallGraph, + lifetimes: MutableMap + ) { assert(lifetimes.isEmpty()) try { val intraproceduralAnalysisResult = IntraproceduralAnalysis(context, moduleDFG, externalModulesDFG, callGraph).analyze() - InterproceduralAnalysis(context, callGraph, intraproceduralAnalysisResult, externalModulesDFG, lifetimes, + InterproceduralAnalysis(context, generationState, callGraph, intraproceduralAnalysisResult, externalModulesDFG, lifetimes, // TODO: This is a bit conservative, but for more aggressive option some support from runtime is // needed (namely, determining that a pointer is from the stack; this is easy for x86 or x64, // but what about all other platforms?). @@ -1811,7 +1818,7 @@ internal object EscapeAnalysis { ).analyze() } catch (t: Throwable) { val extraUserInfo = - """ + """ Please try to disable escape analysis and rerun the build. To do it add the following snippet to the gradle script: kotlin.targets.withType { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt index b80ee6d267e..fefdb696b4b 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/StaticInitializersOptimization.kt @@ -240,7 +240,7 @@ internal object StaticInitializersOptimization { } private val IrFunction.calledInitializer get() = - (body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer? + (body?.statements?.get(0) as? IrCall)?.symbol?.owner?.takeIf { it.isStaticInitializer }?.parent as IrDeclarationContainer? private fun intraproceduralAnalysis( node: CallGraphNode, @@ -520,7 +520,7 @@ internal object StaticInitializersOptimization { } } - fun removeRedundantCalls(context: Context, callGraph: CallGraph, rootSet: Set) { + fun removeRedundantCalls(context: Context, irModule: IrModuleFragment, callGraph: CallGraph, rootSet: Set) { val analysisResult = InterproceduralAnalysis(context, callGraph, rootSet).analyze() var numberOfFunctionsWithGlobalInitializerCall = 0 @@ -532,7 +532,7 @@ internal object StaticInitializersOptimization { var numberOfCallSitesWithExtractedGlobalInitializerCall = 0 var numberOfCallSitesWithExtractedThreadLocalInitializerCall = 0 - context.irModule!!.transformChildren(object : IrElementTransformer { + irModule.transformChildren(object : IrElementTransformer { override fun visitDeclaration(declaration: IrDeclarationBase, data: IrBuilderWithScope?): IrStatement { return super.visitDeclaration(declaration, context.createIrBuilder(declaration.symbol, SYNTHETIC_OFFSET, SYNTHETIC_OFFSET)) } @@ -574,7 +574,7 @@ internal object StaticInitializersOptimization { } }, data = null) - context.irModule!!.transformChildrenVoid(object : IrElementTransformerVoid() { + irModule.transformChildrenVoid(object : IrElementTransformerVoid() { override fun visitFunction(declaration: IrFunction): IrStatement { val body = declaration.body ?: return declaration val statements = (body as IrBlockBody).statements 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 8ad5ada2909..629e333ad03 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 @@ -328,8 +328,8 @@ object KonanFakeOverrideClassFilter : FakeOverrideClassFilter { // This is an alternative to .isObjCClass that doesn't need to walk up all the class heirarchy, // rather it only looks at immediate super class symbols. private fun IrClass.hasInteropSuperClass() = this.superTypes - .mapNotNull { it.classOrNull } - .any { it.isInterop() } + .mapNotNull { it.classOrNull } + .any { it.isInterop() } override fun needToConstructFakeOverrides(clazz: IrClass): Boolean { return !clazz.hasInteropSuperClass() && clazz !is IrLazyClass @@ -736,7 +736,7 @@ internal class KonanIrLinker( } val signature = function.symbol.signature ?: descriptorSignatures[function.descriptor] - ?: error("No signature for ${function.render()}") + ?: error("No signature for ${function.render()}") val inlineFunctionReference = inlineFunctionReferences[signature] ?: error("No inline function reference for ${function.render()}, sig = ${signature.render()}") val fileDeserializationState = fileDeserializationStates[inlineFunctionReference.file] @@ -924,7 +924,7 @@ internal class KonanIrLinker( private val fileMap = mutableMapOf() private fun getIrFile(packageFragment: PackageFragmentDescriptor): IrFile = fileMap.getOrPut(packageFragment) { - IrFileImpl(NaiveSourceBasedFileEntryImpl(IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName), packageFragment).also { + IrFileImpl(NaiveSourceBasedFileEntryImpl(IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName), packageFragment, moduleFragment).also { moduleFragment.files.add(it) } }