diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBinariesResolver.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBinariesResolver.kt new file mode 100644 index 00000000000..7d149b6893d --- /dev/null +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CacheBinariesResolver.kt @@ -0,0 +1,55 @@ +/* + * Copyright 2010-2023 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.konan.target.LinkerOutputKind + +/** + * Check if we should link static caches into an object file before running full linkage. + */ +internal fun shouldPerformPreLink(config: KonanConfig, caches: ResolvedCacheBinaries, linkerOutputKind: LinkerOutputKind): Boolean { + // Pre-link is only useful when producing static library. Otherwise its just a waste of time. + val isStaticLibrary = linkerOutputKind == LinkerOutputKind.STATIC_LIBRARY && + config.isFinalBinary + val enabled = config.cacheSupport.preLinkCaches + val nonEmptyCaches = caches.static.isNotEmpty() + return isStaticLibrary && enabled && nonEmptyCaches +} + +/** + * List of cache binaries that are required for the final artifact. + * [static] is a list of static libraries (e.g. "libcache.a") + * [dynamic] is a list of dynamic libraries (e.g. "libcache.dylib") + */ +internal class ResolvedCacheBinaries(val static: List, val dynamic: List) + +/** + * Find binary files for compiler caches that are actually required for the linkage. + */ +internal fun resolveCacheBinaries( + cachedLibraries: CachedLibraries, + dependenciesTrackingResult: DependenciesTrackingResult, +): ResolvedCacheBinaries { + val staticCaches = mutableListOf() + val dynamicCaches = mutableListOf() + + dependenciesTrackingResult.allCachedBitcodeDependencies.forEach { dependency -> + val library = dependency.library + val cache = cachedLibraries.getLibraryCache(library) + // Maybe turn it into a warning and continue linkage without caches? + ?: error("Library $library is expected to be cached") + + val list = when (cache.kind) { + CachedLibraries.Kind.DYNAMIC -> dynamicCaches + CachedLibraries.Kind.STATIC -> staticCaches + } + + list += if (dependency.kind is DependenciesTracker.DependencyKind.CertainFiles && cache is CachedLibraries.Cache.PerFile) + dependency.kind.files.map { cache.getFileBinaryPath(it) } + else cache.binariesPaths + } + return ResolvedCacheBinaries(static = staticCaches, dynamic = dynamicCaches) +} 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 69c717401a8..01258a6b906 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Linker.kt @@ -1,9 +1,6 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.driver.PhaseContext -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 @@ -166,33 +163,24 @@ internal class Linker( return executable } - private fun shouldPerformPreLink(caches: CachesToLink, linkerOutputKind: LinkerOutputKind): Boolean { - // Pre-link is only useful when producing static library. Otherwise its just a waste of time. - val isStaticLibrary = linkerOutputKind == LinkerOutputKind.STATIC_LIBRARY && - config.isFinalBinary - val enabled = config.cacheSupport.preLinkCaches - val nonEmptyCaches = caches.static.isNotEmpty() - return isStaticLibrary && enabled && nonEmptyCaches - } - private fun determineLinkerInput( objectFiles: List, linkerOutputKind: LinkerOutputKind, dependenciesTrackingResult: DependenciesTrackingResult, ): LinkerInput { - val caches = determineCachesToLink(context, dependenciesTrackingResult) + val caches = resolveCacheBinaries(context.config.cachedLibraries, 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() return when { config.produce == CompilerOutputKind.STATIC_CACHE -> { // Do not link static cache dependencies. - LinkerInput(objectFiles, CachesToLink(emptyList(), caches.dynamic), emptyList(), cachingInvolved) + LinkerInput(objectFiles, ResolvedCacheBinaries(emptyList(), caches.dynamic), emptyList(), cachingInvolved) } - shouldPerformPreLink(caches, linkerOutputKind) -> { + shouldPerformPreLink(config, caches, linkerOutputKind) -> { val preLinkResult = tempFiles.create("withStaticCaches", ".o").absolutePath val preLinkCommands = linker.preLinkCommands(objectFiles + caches.static, preLinkResult) - LinkerInput(listOf(preLinkResult), CachesToLink(emptyList(), caches.dynamic), preLinkCommands, cachingInvolved) + LinkerInput(listOf(preLinkResult), ResolvedCacheBinaries(emptyList(), caches.dynamic), preLinkCommands, cachingInvolved) } else -> LinkerInput(objectFiles, caches, emptyList(), cachingInvolved) } @@ -201,33 +189,7 @@ internal class Linker( private class LinkerInput( val objectFiles: List, - val caches: CachesToLink, + val caches: ResolvedCacheBinaries, val preLinkCommands: List, val cachingInvolved: Boolean -) - -private class CachesToLink(val static: List, val dynamic: List) - -private fun determineCachesToLink( - context: PhaseContext, - dependenciesTrackingResult: DependenciesTrackingResult, -): CachesToLink { - val staticCaches = mutableListOf() - val dynamicCaches = mutableListOf() - - dependenciesTrackingResult.allCachedBitcodeDependencies.forEach { dependency -> - val library = dependency.library - val cache = context.config.cachedLibraries.getLibraryCache(library) - ?: error("Library $library is expected to be cached") - - val list = when (cache.kind) { - CachedLibraries.Kind.DYNAMIC -> dynamicCaches - CachedLibraries.Kind.STATIC -> staticCaches - } - - list += if (dependency.kind is DependenciesTracker.DependencyKind.CertainFiles && cache is CachedLibraries.Cache.PerFile) - dependency.kind.files.map { cache.getFileBinaryPath(it) } - else cache.binariesPaths - } - return CachesToLink(static = staticCaches, dynamic = dynamicCaches) -} +) \ No newline at end of file