diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt index 0e9b6857a5e..5d8175c4520 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2NativeCompilerArguments.kt @@ -421,6 +421,19 @@ class K2NativeCompilerArguments : CommonCompilerArguments() { @Argument(value = "-Xomit-framework-binary", description = "Omit binary when compiling framework") var omitFrameworkBinary: Boolean = false + @Argument(value = "-Xcompile-from-bitcode", description = "Continue compilation from bitcode file", valueDescription = "") + var compileFromBitcode: String? = null + + @Argument( + value = "-Xread-dependencies-from", + description = "Serialized dependencies to use for linking", + valueDescription = "" + ) + var serializedDependencies: String? = null + + @Argument(value = "-Xwrite-dependencies-to", description = "Path for writing backend dependencies") + var saveDependenciesPath: String? = null + @Argument(value = "-Xsave-llvm-ir-directory", description = "Directory that should contain results of -Xsave-llvm-ir-after=") var saveLlvmIrDirectory: String? = null diff --git a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 5ba62ff1f47..7d378310dc7 100644 --- a/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/kotlin-native/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -133,7 +133,7 @@ class K2Native : CLICompiler() { private val K2NativeCompilerArguments.isUsefulWithoutFreeArgs: Boolean get() = listTargets || listPhases || checkDependencies || !includes.isNullOrEmpty() || - libraryToAddToCache != null || !exportedLibraries.isNullOrEmpty() + libraryToAddToCache != null || !exportedLibraries.isNullOrEmpty() || !compileFromBitcode.isNullOrEmpty() // It is executed before doExecute(). override fun setupPlatformSpecificArgumentsAndServices( 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 441544f48bf..8feb2501628 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 @@ -167,7 +167,7 @@ private fun parseAndLinkBitcodeFile(generationState: NativeGenerationState, llvm } } -private fun embedAppleLinkerOptionsToBitcode(llvm: Llvm, config: KonanConfig) { +private fun embedAppleLinkerOptionsToBitcode(llvm: CodegenLlvmHelpers, config: KonanConfig) { fun findEmbeddableOptions(options: List): List> { val result = mutableListOf>() val iterator = options.iterator() 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 e60688ae761..be37c722a9b 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 @@ -371,5 +371,49 @@ internal object DependenciesSerializer { data class DependenciesTrackingResult( val nativeDependenciesToLink: List, val allNativeDependencies: List, - val allCachedBitcodeDependencies: List -) \ No newline at end of file + val allCachedBitcodeDependencies: List) { + + companion object { + private const val NATIVE_DEPENDENCIES_TO_LINK = "NATIVE_DEPENDENCIES_TO_LINK" + private const val ALL_NATIVE_DEPENDENCIES = "ALL_NATIVE_DEPENDENCIES" + private const val ALL_CACHED_BITCODE_DEPENDENCIES = "ALL_CACHED_BITCODE_DEPENDENCIES" + + fun serialize(res: DependenciesTrackingResult): List { + val nativeDepsToLink = DependenciesSerializer.serialize(res.nativeDependenciesToLink.map { DependenciesTracker.ResolvedDependency.wholeModule(it) }) + val allNativeDeps = DependenciesSerializer.serialize(res.allNativeDependencies.map { DependenciesTracker.ResolvedDependency.wholeModule(it) }) + val allCachedBitcodeDeps = DependenciesSerializer.serialize(res.allCachedBitcodeDependencies) + return listOf(NATIVE_DEPENDENCIES_TO_LINK) + nativeDepsToLink + + listOf(ALL_NATIVE_DEPENDENCIES) + allNativeDeps + + listOf(ALL_CACHED_BITCODE_DEPENDENCIES) + allCachedBitcodeDeps + } + + fun deserialize(path: String, dependencies: List, config: KonanConfig): DependenciesTrackingResult { + + val nativeDepsToLinkIndex = dependencies.indexOf(NATIVE_DEPENDENCIES_TO_LINK) + require(nativeDepsToLinkIndex >= 0) { "Invalid dependency file at $path" } + val allNativeDepsIndex = dependencies.indexOf(ALL_NATIVE_DEPENDENCIES) + require(allNativeDepsIndex >= 0) { "Invalid dependency file at $path" } + val allCachedBitcodeDepsIndex = dependencies.indexOf(ALL_CACHED_BITCODE_DEPENDENCIES) + require(allCachedBitcodeDepsIndex >= 0) { "Invalid dependency file at $path" } + + val nativeLibsToLink = DependenciesSerializer.deserialize(path, dependencies.subList(nativeDepsToLinkIndex + 1, allNativeDepsIndex)).map { it.libName } + val allNativeLibs = DependenciesSerializer.deserialize(path, dependencies.subList(allNativeDepsIndex + 1, allCachedBitcodeDepsIndex)).map { it.libName } + val allCachedBitcodeDeps = DependenciesSerializer.deserialize(path, dependencies.subList(allCachedBitcodeDepsIndex + 1, dependencies.size)) + + val topSortedLibraries = config.resolvedLibraries.getFullList(TopologicalLibraryOrder) + val nativeDependenciesToLink = topSortedLibraries.mapNotNull { if (it.uniqueName in nativeLibsToLink && it is KonanLibrary) it else null } + val allNativeDependencies = topSortedLibraries.mapNotNull { if (it.uniqueName in allNativeLibs && it is KonanLibrary) it else null } + val allCachedBitcodeDependencies = allCachedBitcodeDeps.map { unresolvedDep -> + val lib = topSortedLibraries.find { it.uniqueName == unresolvedDep.libName } + require(lib != null && lib is KonanLibrary) { "Invalid dependency ${unresolvedDep.libName} at $path" } + when (unresolvedDep.kind) { + is DependenciesTracker.DependencyKind.CertainFiles -> + DependenciesTracker.ResolvedDependency.certainFiles(lib, unresolvedDep.kind.files) + else -> DependenciesTracker.ResolvedDependency.wholeModule(lib) + } + } + + return DependenciesTrackingResult(nativeDependenciesToLink, allNativeDependencies, allCachedBitcodeDependencies) + } + } +} 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 1252c384983..bbe8129810f 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 @@ -476,11 +476,6 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration } ?: File(outputPath).name - val infoArgsOnly = (configuration.kotlinSourceRoots.isEmpty() - && configuration[KonanConfigKeys.INCLUDED_LIBRARIES].isNullOrEmpty() - && configuration[KonanConfigKeys.EXPORTED_LIBRARIES].isNullOrEmpty() - && libraryToCache == null) - /** * Do not compile binary when compiling framework. * This is useful when user care only about framework's interface. @@ -494,6 +489,40 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration } } + /** + * Continue from bitcode. Skips the frontend and codegen phase of the compiler + * and instead reads the provided bitcode file. + * This option can be used for continuing the compilation from a previous invocation. + */ + internal val compileFromBitcode: String? by lazy { + configuration.get(KonanConfigKeys.COMPILE_FROM_BITCODE) + } + + /** + * Path to serialized dependencies to use for bitcode compilation. + */ + internal val readSerializedDependencies: String? by lazy { + configuration.get(KonanConfigKeys.SERIALIZED_DEPENDENCIES).also { + if (compileFromBitcode.isNullOrEmpty()) { + configuration.report(CompilerMessageSeverity.STRONG_WARNING, + "Providing serialized dependencies only works in conjunction with a bitcode file to compile.") + } + } + } + + /** + * Path to store backend dependency information. + */ + internal val writeSerializedDependencies: String? by lazy { + configuration.get(KonanConfigKeys.SAVE_DEPENDENCIES_PATH) + } + + val infoArgsOnly = (configuration.kotlinSourceRoots.isEmpty() + && configuration[KonanConfigKeys.INCLUDED_LIBRARIES].isNullOrEmpty() + && configuration[KonanConfigKeys.EXPORTED_LIBRARIES].isNullOrEmpty() + && libraryToCache == null && compileFromBitcode.isNullOrEmpty()) + + /** * Directory to store LLVM IR from -Xsave-llvm-ir-after. */ diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt index c9cd2465d10..b2c0590650e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfigurationKeys.kt @@ -161,6 +161,9 @@ class KonanConfigKeys { val PARTIAL_LINKAGE: CompilerConfigurationKey = CompilerConfigurationKey.create("allows some symbols in klibs be missed") val TEST_DUMP_OUTPUT_PATH: CompilerConfigurationKey = CompilerConfigurationKey.create("path to a file to dump the list of all available tests") val OMIT_FRAMEWORK_BINARY: CompilerConfigurationKey = CompilerConfigurationKey.create("do not generate binary in framework") + val COMPILE_FROM_BITCODE: CompilerConfigurationKey = CompilerConfigurationKey.create("path to bitcode file to compile") + val SERIALIZED_DEPENDENCIES: CompilerConfigurationKey = CompilerConfigurationKey.create("path to serialized dependencies for native linking") + val SAVE_DEPENDENCIES_PATH: CompilerConfigurationKey = CompilerConfigurationKey.create("path to save serialized dependencies to") val SAVE_LLVM_IR_DIRECTORY: CompilerConfigurationKey = CompilerConfigurationKey.create("directory to store LLVM IR from phases") } } 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 b47335503e2..a3d60ade008 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 @@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan import llvm.* import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext +import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.utilities.BackendContextHolder import org.jetbrains.kotlin.backend.konan.driver.utilities.LlvmIrHolder import org.jetbrains.kotlin.backend.konan.llvm.* @@ -16,7 +17,6 @@ import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields import org.jetbrains.kotlin.backend.konan.serialization.SerializedEagerInitializedFile import org.jetbrains.kotlin.backend.konan.serialization.SerializedInlineFunctionReference import org.jetbrains.kotlin.ir.declarations.* -import org.jetbrains.kotlin.konan.TempFiles internal class InlineFunctionOriginInfo(val irFunction: IrFunction, val irFile: IrFile, val startOffset: Int, val endOffset: Int) @@ -38,6 +38,19 @@ internal class FileLowerState { "$prefix${cStubCount++}" } +internal interface BitcodePostProcessingContext : PhaseContext, LlvmIrHolder { + val llvm: BasicLlvmHelpers + val llvmContext: LLVMContextRef +} + +internal class BitcodePostProcessingContextImpl( + config: KonanConfig, + override val llvmModule: LLVMModuleRef, + override val llvmContext: LLVMContextRef +) : BitcodePostProcessingContext, BasicPhaseContext(config) { + override val llvm: BasicLlvmHelpers = BasicLlvmHelpers(this, llvmModule) +} + internal class NativeGenerationState( config: KonanConfig, // TODO: Get rid of this property completely once transition to the dynamic driver is complete. @@ -48,7 +61,7 @@ internal class NativeGenerationState( val llvmModuleSpecification: LlvmModuleSpecification, val outputFiles: OutputFiles, val llvmModuleName: String, -) : BasicPhaseContext(config), BackendContextHolder, LlvmIrHolder { +) : BasicPhaseContext(config), BackendContextHolder, LlvmIrHolder, BitcodePostProcessingContext { val outputFile = outputFiles.mainFileName val inlineFunctionBodies = mutableListOf() @@ -72,12 +85,12 @@ internal class NativeGenerationState( val producedLlvmModuleContainsStdlib get() = llvmModuleSpecification.containsModule(context.stdlibModule) private val runtimeDelegate = lazy { Runtime(llvmContext, config.distribution.compilerInterface(config.target)) } - private val llvmDelegate = lazy { Llvm(this, LLVMModuleCreateWithNameInContext(llvmModuleName, llvmContext)!!) } + private val llvmDelegate = lazy { CodegenLlvmHelpers(this, LLVMModuleCreateWithNameInContext(llvmModuleName, llvmContext)!!) } private val debugInfoDelegate = lazy { DebugInfo(this) } - val llvmContext = LLVMContextCreate()!! + override val llvmContext = LLVMContextCreate()!! val runtime by runtimeDelegate - val llvm by llvmDelegate + override val llvm by llvmDelegate val debugInfo by debugInfoDelegate val cStubsManager = CStubsManager(config.target, this) lateinit var llvmDeclarations: LlvmDeclarations diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt index 56ec53e0917..6d28d1961ac 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/SetupConfiguration.kt @@ -262,6 +262,9 @@ fun CompilerConfiguration.setupFromArguments(arguments: K2NativeCompilerArgument arguments.testDumpOutputPath?.let { put(TEST_DUMP_OUTPUT_PATH, it) } put(PARTIAL_LINKAGE, arguments.partialLinkage) put(OMIT_FRAMEWORK_BINARY, arguments.omitFrameworkBinary) + putIfNotNull(COMPILE_FROM_BITCODE, arguments.compileFromBitcode) + putIfNotNull(SERIALIZED_DEPENDENCIES, arguments.serializedDependencies) + putIfNotNull(SAVE_DEPENDENCIES_PATH, arguments.saveDependenciesPath) putIfNotNull(SAVE_LLVM_IR_DIRECTORY, arguments.saveLlvmIrDirectory) } 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 81dc38f20b7..0703696aea0 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 @@ -12,7 +12,7 @@ import llvm.LLVMStoreSizeOfType import org.jetbrains.kotlin.backend.common.lower.coroutines.getOrCreateFunctionWithContinuationStub import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.ir.* -import org.jetbrains.kotlin.backend.konan.llvm.Llvm +import org.jetbrains.kotlin.backend.konan.llvm.CodegenLlvmHelpers import org.jetbrains.kotlin.backend.konan.llvm.computeFunctionName import org.jetbrains.kotlin.backend.konan.llvm.toLLVMType import org.jetbrains.kotlin.backend.konan.llvm.localHash @@ -263,7 +263,7 @@ internal class GlobalHierarchyAnalysis(val context: Context, val irModule: IrMod } } -internal fun IrField.requiredAlignment(llvm: Llvm): Int { +internal fun IrField.requiredAlignment(llvm: CodegenLlvmHelpers): 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 @@ -283,7 +283,7 @@ internal fun IrField.requiredAlignment(llvm: Llvm): Int { internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { - private fun IrField.toFieldInfo(llvm: Llvm): FieldInfo { + private fun IrField.toFieldInfo(llvm: CodegenLlvmHelpers): 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(llvm)) @@ -435,7 +435,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { * All fields of the class instance. * The order respects the class hierarchy, i.e. a class [fields] contains superclass [fields] as a prefix. */ - fun getFields(llvm: Llvm): List = getFieldsInternal(llvm).map { fieldInfo -> + fun getFields(llvm: CodegenLlvmHelpers): List = getFieldsInternal(llvm).map { fieldInfo -> val mappedField = fieldInfo.irField?.let { context.mapping.lateInitFieldToNullableField[it] ?: it } if (mappedField == fieldInfo.irField) fieldInfo @@ -445,7 +445,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { private var fields: List? = null - private fun getFieldsInternal(llvm: Llvm): List { + private fun getFieldsInternal(llvm: CodegenLlvmHelpers): List { fields?.let { return it } val superClass = irClass.getSuperClassNotAny() @@ -503,7 +503,7 @@ internal class ClassLayoutBuilder(val irClass: IrClass, val context: Context) { /** * Fields declared in the class. */ - fun getDeclaredFields(llvm: Llvm): List { + fun getDeclaredFields(llvm: CodegenLlvmHelpers): List { val outerThisField = if (irClass.isInner) context.innerClassesSupport.getOuterThisField(irClass) else null 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 962aa158d95..763c8a88102 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 @@ -6,14 +6,20 @@ package org.jetbrains.kotlin.backend.konan.driver import kotlinx.cinterop.usingJvmCInteropCallbacks +import llvm.LLVMContextCreate +import llvm.LLVMContextDispose +import llvm.LLVMDisposeModule +import org.jetbrains.kotlin.backend.konan.* +import org.jetbrains.kotlin.backend.konan.BitcodePostProcessingContextImpl 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.backend.konan.llvm.parseBitcodeFile import org.jetbrains.kotlin.builtins.konan.KonanBuiltIns +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.config.CommonConfigurationKeys +import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.util.usingNativeMemoryAllocator @@ -26,7 +32,8 @@ internal class DynamicCompilerDriver : CompilerDriver() { usingNativeMemoryAllocator { usingJvmCInteropCallbacks { PhaseEngine.startTopLevel(config) { engine -> - when (config.produce) { + if (!config.compileFromBitcode.isNullOrEmpty()) produceBinaryFromBitcode(engine, config, config.compileFromBitcode!!) + else when (config.produce) { CompilerOutputKind.PROGRAM -> produceBinary(engine, config, environment) CompilerOutputKind.DYNAMIC -> produceCLibrary(engine, config, environment) CompilerOutputKind.STATIC -> produceCLibrary(engine, config, environment) @@ -124,6 +131,22 @@ internal class DynamicCompilerDriver : CompilerDriver() { engine.runBackend(backendContext, psiToIrOutput.irModule) } + private fun produceBinaryFromBitcode(engine: PhaseEngine, config: KonanConfig, bitcodeFilePath: String) { + val llvmContext = LLVMContextCreate()!! + val llvmModule = parseBitcodeFile(llvmContext, bitcodeFilePath) + try { + val context = BitcodePostProcessingContextImpl(config, llvmModule, llvmContext) + val depsPath = config.readSerializedDependencies + val dependencies = if (depsPath.isNullOrEmpty()) DependenciesTrackingResult(emptyList(), emptyList(), emptyList()).also { + config.configuration.report(CompilerMessageSeverity.WARNING, "No backend dependencies provided.") + } else DependenciesTrackingResult.deserialize(depsPath, File(depsPath).readStrings(), config) + engine.runBitcodeBackend(context, dependencies) + } finally { + LLVMDisposeModule(llvmModule) + LLVMContextDispose(llvmContext) + } + } + private fun createBackendContext( config: KonanConfig, frontendOutput: FrontendPhaseOutput.Full, 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 90612ec56b4..5671d8a130a 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 @@ -118,6 +118,15 @@ internal class PhaseEngine( } } + /** + * Create a new PhaseEngine instance for an existing context that should not be disposed after the action. + * This is useful for creating engines for a sub/super context type. + */ + inline fun newEngine(newContext: T, action: (PhaseEngine) -> R): R { + val newEngine = PhaseEngine(phaseConfig, phaserState, newContext) + return action(newEngine) + } + fun > runPhase( phase: P, input: Input, 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 82187a7abea..cc97add689d 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 @@ -108,7 +108,7 @@ internal val CoveragePhase = createSimpleNamedCompilerPhase runCoveragePass(context) } ) -internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase( +internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase( name = "RemoveRedundantSafepoints", description = "Remove function prologue safepoints inlined to another function", postactions = getDefaultLlvmModuleActions(), @@ -120,7 +120,7 @@ internal val RemoveRedundantSafepointsPhase = createSimpleNamedCompilerPhase( +internal val OptimizeTLSDataLoadsPhase = createSimpleNamedCompilerPhase( name = "OptimizeTLSDataLoads", description = "Optimize multiple loads of thread data", postactions = getDefaultLlvmModuleActions(), @@ -153,15 +153,11 @@ internal val PrintBitcodePhase = createSimpleNamedCompilerPhase LLVMDumpModule(llvmModule) } ) -internal fun PhaseEngine.runBitcodePostProcessing() { - val checkExternalCalls = context.config.configuration.getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS) - if (checkExternalCalls) { - runPhase(CheckExternalCallsPhase) - } +internal fun PhaseEngine.runBitcodePostProcessing() { val optimizationConfig = createLTOFinalPipelineConfig( context, context.llvm.targetTriple, - closedWorld = context.llvmModuleSpecification.isFinal, + closedWorld = context.config.isFinalBinary, timePasses = context.config.flexiblePhaseConfig.needProfiling, ) useContext(OptimizationState(context.config, optimizationConfig)) { @@ -175,14 +171,14 @@ internal fun PhaseEngine.runBitcodePostProcessing() { null -> {} } } - runPhase(CoveragePhase) + val checkExternalCalls = context.config.configuration.getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS) + if (checkExternalCalls && context is NativeGenerationState) { + newEngine(context) { it.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/TopLevelPhases.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/driver/phases/TopLevelPhases.kt index 2375a0002fb..ed5d04ccb5e 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 @@ -23,7 +23,7 @@ import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.library.impl.javaFile -import java.io.File +import org.jetbrains.kotlin.konan.file.File internal fun PhaseEngine.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? { val frontendOutput = useContext(FrontendContextImpl(config)) { it.runPhase(FrontendPhase, environment) } @@ -80,7 +80,12 @@ internal fun PhaseEngine.runBackend(backendContext: Contex // TODO: Make this work if we first compile all the fragments and only after that run the link phases. generationStateEngine.compileModule(fragment.irModule, bitcodeFile, cExportFiles) // Split here - val moduleCompilationOutput = ModuleCompilationOutput(bitcodeFile, generationState.dependenciesTracker.collectResult()) + val dependenciesTrackingResult = generationState.dependenciesTracker.collectResult() + val depsFilePath = config.writeSerializedDependencies + if (!depsFilePath.isNullOrEmpty()) { + depsFilePath.File().writeLines(DependenciesTrackingResult.serialize(dependenciesTrackingResult)) + } + val moduleCompilationOutput = ModuleCompilationOutput(bitcodeFile, dependenciesTrackingResult) compileAndLink(moduleCompilationOutput, outputFiles.mainFileName, outputFiles, tempFiles, isCoverageEnabled = false) } } finally { @@ -90,6 +95,19 @@ internal fun PhaseEngine.runBackend(backendContext: Contex } } +internal fun PhaseEngine.runBitcodeBackend(context: BitcodePostProcessingContext, dependencies: DependenciesTrackingResult) { + useContext(context) { bitcodeEngine -> + val tempFiles = createTempFiles(context.config, null) + val bitcodeFile = tempFiles.create(context.config.shortModuleName ?: "out", ".bc").javaFile() + val outputPath = context.config.outputPath + val outputFiles = OutputFiles(outputPath, context.config.target, context.config.produce) + bitcodeEngine.runBitcodePostProcessing() + runPhase(WriteBitcodeFilePhase, WriteBitcodeFileInput(context.llvm.module, bitcodeFile)) + val moduleCompilationOutput = ModuleCompilationOutput(bitcodeFile, dependencies) + compileAndLink(moduleCompilationOutput, outputFiles.mainFileName, outputFiles, tempFiles, isCoverageEnabled = false) + } +} + private fun isReferencedByNativeRuntime(declarations: List): Boolean = declarations.any { it.hasAnnotation(RuntimeNames.exportTypeInfoAnnotation) @@ -159,7 +177,7 @@ private fun PhaseEngine.splitIntoFragments( } internal data class ModuleCompilationOutput( - val bitcodeFile: File, + val bitcodeFile: java.io.File, val dependenciesTrackingResult: DependenciesTrackingResult, ) @@ -170,7 +188,7 @@ internal data class ModuleCompilationOutput( * 4. Optimizes it. * 5. Serializes it to a bitcode file. */ -internal fun PhaseEngine.compileModule(module: IrModuleFragment, bitcodeFile: File, cExportFiles: CExportFiles?) { +internal fun PhaseEngine.compileModule(module: IrModuleFragment, bitcodeFile: java.io.File, cExportFiles: CExportFiles?) { if (context.config.produce.isCache) { runPhase(BuildAdditionalCacheInfoPhase, module) } @@ -178,13 +196,21 @@ internal fun PhaseEngine.compileModule(module: IrModuleFr runPhase(EntryPointPhase, module) } runBackendCodegen(module, cExportFiles) - runBitcodePostProcessing() + val checkExternalCalls = context.config.configuration.getBoolean(KonanConfigKeys.CHECK_EXTERNAL_CALLS) + if (checkExternalCalls) { + runPhase(CheckExternalCallsPhase) + } + newEngine(context as BitcodePostProcessingContext) { it.runBitcodePostProcessing() } + if (checkExternalCalls) { + runPhase(RewriteExternalCallsCheckerGlobals) + } if (context.config.produce.isCache) { runPhase(SaveAdditionalCacheInfoPhase) } runPhase(WriteBitcodeFilePhase, WriteBitcodeFileInput(context.llvm.module, bitcodeFile)) } + internal fun PhaseEngine.compileAndLink( moduleCompilationOutput: ModuleCompilationOutput, linkerOutputFile: String, diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 09b895a0c4f..d0b33a19e2e 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -1298,7 +1298,7 @@ internal abstract class FunctionGenerationContext( assert(!irClass.isInterface) return if (irClass.isExternalObjCClass()) { - llvm.dependenciesTracker.add(irClass) + generationState.dependenciesTracker.add(irClass) if (irClass.isObjCMetaClass()) { val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() val objCClass = getObjCClass(name) @@ -1337,7 +1337,7 @@ internal abstract class FunctionGenerationContext( private fun getObjCClass(binaryName: String) = load(codegen.objCDataGenerator!!.genClassRef(binaryName).llvm) fun getObjCClassFromNativeRuntime(binaryName: String): LLVMValueRef { - llvm.dependenciesTracker.addNativeRuntime() + generationState.dependenciesTracker.addNativeRuntime() return getObjCClass(binaryName) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index d3b39c4c300..b9d2c3a29e7 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -148,7 +148,7 @@ internal interface ContextUtils : RuntimeAware { val llvmTargetData: LLVMTargetDataRef get() = runtime.targetData - val llvm: Llvm + val llvm: CodegenLlvmHelpers get() = generationState.llvm val staticData: KotlinStaticData @@ -256,42 +256,61 @@ internal class InitializersGenerationState { } } -internal class ConstInt1(llvm: Llvm, val value: Boolean) : ConstValue { +internal class ConstInt1(llvm: CodegenLlvmHelpers, val value: Boolean) : ConstValue { override val llvm = LLVMConstInt(llvm.int1Type, if (value) 1 else 0, 1)!! } -internal class ConstInt8(llvm: Llvm, val value: Byte) : ConstValue { +internal class ConstInt8(llvm: CodegenLlvmHelpers, val value: Byte) : ConstValue { override val llvm = LLVMConstInt(llvm.int8Type, value.toLong(), 1)!! } -internal class ConstInt16(llvm: Llvm, val value: Short) : ConstValue { +internal class ConstInt16(llvm: CodegenLlvmHelpers, val value: Short) : ConstValue { override val llvm = LLVMConstInt(llvm.int16Type, value.toLong(), 1)!! } -internal class ConstChar16(llvm: Llvm, val value: Char) : ConstValue { +internal class ConstChar16(llvm: CodegenLlvmHelpers, val value: Char) : ConstValue { override val llvm = LLVMConstInt(llvm.int16Type, value.code.toLong(), 1)!! } -internal class ConstInt32(llvm: Llvm, val value: Int) : ConstValue { +internal class ConstInt32(llvm: CodegenLlvmHelpers, val value: Int) : ConstValue { override val llvm = LLVMConstInt(llvm.int32Type, value.toLong(), 1)!! } -internal class ConstInt64(llvm: Llvm, val value: Long) : ConstValue { +internal class ConstInt64(llvm: CodegenLlvmHelpers, val value: Long) : ConstValue { override val llvm = LLVMConstInt(llvm.int64Type, value, 1)!! } -internal class ConstFloat32(llvm: Llvm, val value: Float) : ConstValue { +internal class ConstFloat32(llvm: CodegenLlvmHelpers, val value: Float) : ConstValue { override val llvm = LLVMConstReal(llvm.floatType, value.toDouble())!! } -internal class ConstFloat64(llvm: Llvm, val value: Double) : ConstValue { +internal class ConstFloat64(llvm: CodegenLlvmHelpers, val value: Double) : ConstValue { override val llvm = LLVMConstReal(llvm.doubleType, value)!! } +internal open class BasicLlvmHelpers(bitcodeContext: BitcodePostProcessingContext, val module: LLVMModuleRef) { + + val llvmContext = bitcodeContext.llvmContext + val targetTriple by lazy { + LLVMGetTarget(module)!!.toKString() + } + + val runtimeAnnotationMap by lazy { + StaticData.getGlobal(module, "llvm.global.annotations") + ?.getInitializer() + ?.let { getOperands(it) } + ?.groupBy( + { LLVMGetInitializer(LLVMGetOperand(LLVMGetOperand(it, 1), 0))?.getAsCString() ?: "" }, + { LLVMGetOperand(LLVMGetOperand(it, 0), 0)!! } + ) + ?.filterKeys { it != "" } + ?: emptyMap() + } +} + @Suppress("FunctionName", "PropertyName", "PrivatePropertyName") -internal class Llvm(private val generationState: NativeGenerationState, val module: LLVMModuleRef) : RuntimeAware { +internal class CodegenLlvmHelpers(private val generationState: NativeGenerationState, module: LLVMModuleRef) : BasicLlvmHelpers(generationState, module), RuntimeAware { private val context = generationState.context - val llvmContext = generationState.llvmContext private fun importFunction(name: String, otherModule: LLVMModuleRef): LlvmCallable { if (LLVMGetNamedFunction(module, name) != null) { @@ -378,11 +397,9 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu override val runtime get() = generationState.runtime - val targetTriple = runtime.target - init { LLVMSetDataLayout(module, runtime.dataLayout) - LLVMSetTarget(module, targetTriple) + LLVMSetTarget(module, runtime.target) } private fun importRtFunction(name: String) = importFunction(name, runtime.llvmModule) @@ -480,27 +497,14 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu val initializersGenerationState = InitializersGenerationState() val boxCacheGlobals = mutableMapOf() - val runtimeAnnotationMap by lazy { - staticData.getGlobal("llvm.global.annotations") - ?.getInitializer() - ?.let { getOperands(it) } - ?.groupBy( - { LLVMGetInitializer(LLVMGetOperand(LLVMGetOperand(it, 1), 0))?.getAsCString() ?: "" }, - { LLVMGetOperand(LLVMGetOperand(it, 0), 0)!! } - ) - ?.filterKeys { it != "" } - ?: emptyMap() - } - - private object lazyRtFunction { operator fun provideDelegate( - thisRef: Llvm, property: KProperty<*> - ) = object : ReadOnlyProperty { + thisRef: CodegenLlvmHelpers, property: KProperty<*> + ) = object : ReadOnlyProperty { val value: LlvmCallable by lazy { thisRef.importRtFunction(property.name) } - override fun getValue(thisRef: Llvm, property: KProperty<*>): LlvmCallable = value + override fun getValue(thisRef: CodegenLlvmHelpers, property: KProperty<*>): LlvmCallable = value } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt index a9e8ccd5c8b..4bc2afbafec 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/DataLayout.kt @@ -11,7 +11,7 @@ import org.jetbrains.kotlin.ir.types.IrType import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isUnit -private fun PrimitiveBinaryType?.toLlvmType(llvm: Llvm) = when (this) { +private fun PrimitiveBinaryType?.toLlvmType(llvm: CodegenLlvmHelpers) = when (this) { null -> llvm.kObjHeaderPtr PrimitiveBinaryType.BOOLEAN -> llvm.int1Type @@ -26,12 +26,12 @@ private fun PrimitiveBinaryType?.toLlvmType(llvm: Llvm) = when (this) { PrimitiveBinaryType.POINTER -> llvm.int8PtrType } -internal fun IrType.toLLVMType(llvm: Llvm): LLVMTypeRef = +internal fun IrType.toLLVMType(llvm: CodegenLlvmHelpers): LLVMTypeRef = llvm.runtime.calculatedLLVMTypes.getOrPut(this) { computePrimitiveBinaryTypeOrNull().toLlvmType(llvm) } internal fun IrType.isVoidAsReturnType() = isUnit() || isNothing() -internal fun IrType.getLLVMReturnType(llvm: Llvm) = when { +internal fun IrType.getLLVMReturnType(llvm: CodegenLlvmHelpers) = when { isVoidAsReturnType() -> llvm.voidType else -> toLLVMType(llvm) } 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 a67b6ca90b3..3a9cb48a9cb 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 @@ -40,7 +40,6 @@ import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.uniqueName -import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.descriptorUtil.classId @@ -472,7 +471,7 @@ internal class CodeGeneratorVisitor( private fun createInitBody(state: ScopeInitializersGenerationState): LLVMValueRef { val initFunction = addLlvmFunctionWithDefaultAttributes( - context, + generationState.context, llvm.module, "", kInitFuncType @@ -551,7 +550,7 @@ internal class CodeGeneratorVisitor( // Create static object of class InitNode. val initNode = LLVMConstNamedStruct(kNodeInitType, argList, 2)!! // Create global variable with init record data. - return llvm.staticData.placeGlobal("init_node", constPointer(initNode), isExported = false).llvmGlobal + return codegen.staticData.placeGlobal("init_node", constPointer(initNode), isExported = false).llvmGlobal } //-------------------------------------------------------------------------// @@ -842,7 +841,7 @@ internal class CodeGeneratorVisitor( recordCoverage(body) if (declaration.isReifiedInline) { callDirect(context.ir.symbols.throwIllegalStateExceptionWithMessage.owner, - listOf(llvm.staticData.kotlinStringLiteral( + listOf(codegen.staticData.kotlinStringLiteral( "unsupported call of reified inlined function `${declaration.fqNameForIrSerialization}`").llvm), Lifetime.IRRELEVANT, null) return@usingVariableScope @@ -1288,7 +1287,7 @@ internal class CodeGeneratorVisitor( functionGenerationContext.positionAtEnd(whenEmittingContext.bbExit.value) return when { - expression.type.isUnit() -> functionGenerationContext.theUnitInstanceRef.llvm + expression.type.isUnit() -> codegen.theUnitInstanceRef.llvm expression.type.isNothing() -> functionGenerationContext.kNothingFakeValue whenEmittingContext.resultPhi.isInitialized() -> whenEmittingContext.resultPhi.value else -> LLVMGetUndef(whenEmittingContext.llvmType)!! @@ -1346,7 +1345,7 @@ internal class CodeGeneratorVisitor( } assert(loop.type.isUnit()) - return functionGenerationContext.theUnitInstanceRef.llvm + return codegen.theUnitInstanceRef.llvm } //-------------------------------------------------------------------------// @@ -1371,7 +1370,7 @@ internal class CodeGeneratorVisitor( } assert(loop.type.isUnit()) - return functionGenerationContext.theUnitInstanceRef.llvm + return codegen.theUnitInstanceRef.llvm } //-------------------------------------------------------------------------// @@ -1395,7 +1394,7 @@ internal class CodeGeneratorVisitor( val variable = currentCodeContext.getDeclaredValue(value.symbol.owner) functionGenerationContext.vars.store(result, variable) assert(value.type.isUnit()) - return functionGenerationContext.theUnitInstanceRef.llvm + return codegen.theUnitInstanceRef.llvm } //-------------------------------------------------------------------------// @@ -1465,7 +1464,7 @@ internal class CodeGeneratorVisitor( IrTypeOperator.IMPLICIT_NOTNULL -> TODO(ir2string(value)) IrTypeOperator.IMPLICIT_COERCION_TO_UNIT -> { evaluateExpression(value.argument) - functionGenerationContext.theUnitInstanceRef.llvm + codegen.theUnitInstanceRef.llvm } IrTypeOperator.SAFE_CAST -> throw IllegalStateException("safe cast wasn't lowered") IrTypeOperator.INSTANCEOF -> evaluateInstanceOf(value) @@ -1533,7 +1532,7 @@ internal class CodeGeneratorVisitor( val dstFullClassName = dstClass.fqNameWhenAvailable?.toString() ?: dstClass.name.toString() callDirect( context.ir.symbols.throwTypeCastException.owner, - listOf(srcArg, llvm.staticData.kotlinStringLiteral(dstFullClassName).llvm), + listOf(srcArg, codegen.staticData.kotlinStringLiteral(dstFullClassName).llvm), Lifetime.GLOBAL, null ) @@ -1810,7 +1809,7 @@ internal class CodeGeneratorVisitor( //-------------------------------------------------------------------------// private fun evaluateStringConst(value: IrConst) = - llvm.staticData.kotlinStringLiteral(value.value) + codegen.staticData.kotlinStringLiteral(value.value) private fun evaluateConst(value: IrConst<*>): ConstValue { context.log{"evaluateConst : ${ir2string(value)}"} @@ -1862,7 +1861,7 @@ internal class CodeGeneratorVisitor( require(value.type.toLLVMType(llvm) == codegen.kObjHeaderPtr) { "Can't wrap ${value.value.kind.asString} constant to type ${value.type.render()}" } - value.toBoxCacheValue(generationState) ?: llvm.staticData.createConstKotlinObject( + value.toBoxCacheValue(generationState) ?: codegen.staticData.createConstKotlinObject( constructedType.getClass()!!, evaluateConst(value.value) ) @@ -1876,7 +1875,7 @@ internal class CodeGeneratorVisitor( require(clazz.symbol == symbols.array || clazz.symbol in symbols.primitiveTypesToPrimitiveArrays.values) { "Statically initialized array should have array type" } - llvm.staticData.createConstKotlinArray( + codegen.staticData.createConstKotlinArray( value.type.getClass()!!, value.elements.map { evaluateConstantValue(it) } ) @@ -1937,7 +1936,7 @@ internal class CodeGeneratorVisitor( } require(value.type.toLLVMType(llvm) == codegen.kObjHeaderPtr) { "Constant object is not an object, but ${value.type.render()}" } - llvm.staticData.createConstKotlinObject( + codegen.staticData.createConstKotlinObject( constructedClass, *fields.toTypedArray() ) @@ -2717,7 +2716,7 @@ internal class CodeGeneratorVisitor( if (args.isEmpty()) return val argsCasted = args.map { constPointer(it).bitcast(llvm.int8PtrType) } - val llvmUsedGlobal = llvm.staticData.placeGlobalArray(name, llvm.int8PtrType, argsCasted) + val llvmUsedGlobal = codegen.staticData.placeGlobalArray(name, llvm.int8PtrType, argsCasted) LLVMSetLinkage(llvmUsedGlobal.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) LLVMSetSection(llvmUsedGlobal.llvmGlobal, "llvm.metadata") @@ -2802,7 +2801,7 @@ internal class CodeGeneratorVisitor( fun addCtorFunction(ctorName: String) = addLlvmFunctionWithDefaultAttributes( - context, + generationState.context, llvm.module, ctorName, kVoidFuncType @@ -2898,7 +2897,7 @@ internal class CodeGeneratorVisitor( LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMPrivateLinkage) // Append initializers of global variables in "llvm.global_ctors" array. - val globalCtors = llvm.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, + val globalCtors = codegen.staticData.placeGlobalArray("llvm.global_ctors", kCtorType, listOf(createGlobalCtor(globalCtorFunction))) LLVMSetLinkage(globalCtors.llvmGlobal, LLVMLinkage.LLVMAppendingLinkage) if (context.config.produce == CompilerOutputKind.PROGRAM) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt index 18d1f0de06e..f3d0297c8e0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/KotlinStaticData.kt @@ -7,9 +7,7 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.cValuesOf import llvm.* -import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.NativeGenerationState -import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.expressions.IrConst @@ -17,7 +15,7 @@ private fun ConstPointer.add(index: LLVMValueRef): ConstPointer { return constPointer(LLVMConstGEP(llvm, cValuesOf(index), 1)!!) } -internal class KotlinStaticData(override val generationState: NativeGenerationState, override val llvm: Llvm, module: LLVMModuleRef) : ContextUtils, StaticData(module, llvm) { +internal class KotlinStaticData(override val generationState: NativeGenerationState, override val llvm: CodegenLlvmHelpers, module: LLVMModuleRef) : ContextUtils, StaticData(module, llvm) { private val stringLiterals = mutableMapOf() // Must match OBJECT_TAG_PERMANENT_CONTAINER in C++. diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt index 4775b6cab37..06b1766221c 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/LlvmUtils.kt @@ -23,7 +23,7 @@ internal val ConstValue.llvmType: LLVMTypeRef get() = this.llvm.type internal interface ConstPointer : ConstValue { - fun getElementPtr(llvm: Llvm, index: Int): ConstPointer = ConstGetElementPtr(llvm, this, index) + fun getElementPtr(llvm: CodegenLlvmHelpers, index: Int): ConstPointer = ConstGetElementPtr(llvm, this, index) } internal fun constPointer(value: LLVMValueRef) = object : ConstPointer { @@ -34,7 +34,7 @@ internal fun constPointer(value: LLVMValueRef) = object : ConstPointer { override val llvm = value } -private class ConstGetElementPtr(llvm: Llvm, pointer: ConstPointer, index: Int) : ConstPointer { +private class ConstGetElementPtr(llvm: CodegenLlvmHelpers, pointer: ConstPointer, index: Int) : ConstPointer { override val llvm = LLVMConstInBoundsGEP(pointer.llvm, cValuesOf(llvm.int32(0), llvm.int32(index)), 2)!! // TODO: squash multiple GEPs } @@ -182,12 +182,12 @@ private fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef): LLVMValu } internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, declaration: IrDeclaration) = - importGlobal(name, type).also { llvm.dependenciesTracker.add(declaration) } + importGlobal(name, type).also { generationState.dependenciesTracker.add(declaration) } internal fun ContextUtils.importObjCGlobal(name: String, type: LLVMTypeRef) = importGlobal(name, type) internal fun ContextUtils.importNativeRuntimeGlobal(name: String, type: LLVMTypeRef) = - importGlobal(name, type).also { llvm.dependenciesTracker.addNativeRuntime() } + importGlobal(name, type).also { generationState.dependenciesTracker.addNativeRuntime() } private fun CodeGenerator.replaceExternalWeakOrCommonGlobal(name: String, value: ConstValue) { if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt index 6a867095011..2ce7ace21ed 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/StaticData.kt @@ -10,7 +10,7 @@ import llvm.* /** * Provides utilities to create static data. */ -internal open class StaticData(val module: LLVMModuleRef, private val llvm: Llvm) { +internal open class StaticData(val module: LLVMModuleRef, private val llvm: CodegenLlvmHelpers) { /** * Represents the LLVM global variable. @@ -52,6 +52,11 @@ internal open class StaticData(val module: LLVMModuleRef, private val llvm: Llvm val llvmGlobal = LLVMGetNamedGlobal(staticData.module, name) ?: return null return Global(llvmGlobal) } + + fun get(module: LLVMModuleRef, name: String): Global? { + val llvmGlobal = LLVMGetNamedGlobal(module, name) ?: return null + return Global(llvmGlobal) + } } val type get() = getGlobalType(this.llvmGlobal) @@ -150,4 +155,8 @@ internal open class StaticData(val module: LLVMModuleRef, private val llvm: Llvm } internal fun cStringLiteral(value: String) = cStringLiterals.getOrPut(value) { placeCStringLiteral(value) } + + companion object { + fun getGlobal(module: LLVMModuleRef, name: String) = Global.get(module, name) + } } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt index 96fd623bcbe..2f21ae667f0 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/ObjCDataGenerator.kt @@ -116,7 +116,7 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) { ) val globalName = "\u0001l_OBJC_\$_INSTANCE_METHODS_$name" - val global = llvm.staticData.placeGlobal(globalName, methodList).also { + val global = codegen.staticData.placeGlobal(globalName, methodList).also { it.setLinkage(LLVMLinkage.LLVMPrivateLinkage) it.setAlignment(runtime.pointerAlignment) it.setSection("__DATA, __objc_const") @@ -165,7 +165,7 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) { "\u0001l_OBJC_CLASS_RO_\$_" } + name - val roGlobal = llvm.staticData.placeGlobal(roLabel, roValue).also { + val roGlobal = codegen.staticData.placeGlobal(roLabel, roValue).also { it.setLinkage(LLVMLinkage.LLVMPrivateLinkage) it.setAlignment(runtime.pointerAlignment) it.setSection("__DATA, __objc_const") @@ -223,7 +223,7 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) { private fun addModuleClassList(elements: List, name: String, section: String) { if (elements.isEmpty()) return - val global = llvm.staticData.placeGlobalArray( + val global = codegen.staticData.placeGlobalArray( name, llvm.int8PtrType, elements.map { it.bitcast(llvm.int8PtrType) } @@ -270,7 +270,7 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) { } class CStringLiteralsGenerator(val label: String, val section: String) { - fun generate(module: LLVMModuleRef, llvm: Llvm, value: String): ConstPointer { + fun generate(module: LLVMModuleRef, llvm: CodegenLlvmHelpers, value: String): ConstPointer { val bytes = value.toByteArray(Charsets.UTF_8).map { llvm.constInt8(it) } + llvm.constInt8(0) val initializer = ConstArray(llvm.int8Type, bytes) val llvmGlobal = LLVMAddGlobal(module, initializer.llvmType, label)!! diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt index 8274f17b5b7..43d9980de1f 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objc/linkObjC.kt @@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.konan.llvm.objc import kotlinx.cinterop.* import llvm.* -import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.backend.konan.isFinalBinary import org.jetbrains.kotlin.backend.konan.llvm.* @@ -127,7 +126,7 @@ private fun PatchBuilder.addObjCPatches() { } } -private fun PatchBuilder.buildAndApply(llvmModule: LLVMModuleRef, llvm: Llvm) { +private fun PatchBuilder.buildAndApply(llvmModule: LLVMModuleRef, llvm: CodegenLlvmHelpers) { val nameToGlobalPatch = globalPatches.associateNonRepeatingBy { it.globalName } val sectionToValueToLiteralPatch = literalPatches.groupBy { it.generator.section } @@ -197,10 +196,10 @@ private fun List.associateNonRepeatingBy(keySelector: (T) -> K): Map llvm.kNullInt8Ptr is ValueTypeBridge -> LLVMConstNull(this.objCValueType.toLlvmType(llvm))!! } @@ -1929,7 +1929,7 @@ private fun objCFunctionType(generationState: NativeGenerationState, methodBridg return LlvmFunctionSignature(returnType, paramTypes, isVararg = false) } -private fun ObjCValueType.toLlvmType(llvm: Llvm): LLVMTypeRef = when (this) { +private fun ObjCValueType.toLlvmType(llvm: CodegenLlvmHelpers): LLVMTypeRef = when (this) { ObjCValueType.BOOL -> llvm.int8Type ObjCValueType.UNICHAR -> llvm.int16Type ObjCValueType.CHAR -> llvm.int8Type @@ -1945,7 +1945,7 @@ private fun ObjCValueType.toLlvmType(llvm: Llvm): LLVMTypeRef = when (this) { ObjCValueType.POINTER -> llvm.int8PtrType } -private fun MethodBridgeParameter.toLlvmParamType(llvm: Llvm): LlvmParamType = when (this) { +private fun MethodBridgeParameter.toLlvmParamType(llvm: CodegenLlvmHelpers): LlvmParamType = when (this) { is MethodBridgeValueParameter.Mapped -> this.bridge.toLlvmParamType(llvm) is MethodBridgeReceiver -> ReferenceBridge.toLlvmParamType(llvm) MethodBridgeSelector -> LlvmParamType(llvm.int8PtrType) @@ -1972,7 +1972,7 @@ private fun MethodBridge.ReturnValue.toLlvmRetType( } } -private fun TypeBridge.toLlvmParamType(llvm: Llvm): LlvmParamType = when (this) { +private fun TypeBridge.toLlvmParamType(llvm: CodegenLlvmHelpers): LlvmParamType = when (this) { is ReferenceBridge, is BlockPointerBridge -> LlvmParamType(llvm.int8PtrType) is ValueTypeBridge -> LlvmParamType(this.objCValueType.toLlvmType(llvm), this.objCValueType.defaultParameterAttributes) } diff --git a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveMultipleThreadDataLoads.kt b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveMultipleThreadDataLoads.kt index e19287e6f68..b6e575dccaa 100644 --- a/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveMultipleThreadDataLoads.kt +++ b/kotlin-native/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/optimizations/RemoveMultipleThreadDataLoads.kt @@ -6,8 +6,7 @@ package org.jetbrains.kotlin.backend.konan.optimizations import llvm.* -import org.jetbrains.kotlin.backend.konan.Context -import org.jetbrains.kotlin.backend.konan.NativeGenerationState +import org.jetbrains.kotlin.backend.konan.BitcodePostProcessingContext import org.jetbrains.kotlin.backend.konan.llvm.getBasicBlocks import org.jetbrains.kotlin.backend.konan.llvm.getFunctions import org.jetbrains.kotlin.backend.konan.llvm.getInstructions @@ -32,10 +31,10 @@ private fun process(function: LLVMValueRef, currentThreadTLV: LLVMValueRef) { } } -internal fun removeMultipleThreadDataLoads(generationState: NativeGenerationState) { - val currentThreadTLV = generationState.llvm.runtimeAnnotationMap["current_thread_tlv"]?.singleOrNull() ?: return +internal fun removeMultipleThreadDataLoads(context: BitcodePostProcessingContext) { + val currentThreadTLV = context.llvm.runtimeAnnotationMap["current_thread_tlv"]?.singleOrNull() ?: return - getFunctions(generationState.llvm.module) + getFunctions(context.llvm.module) .filter { it.name?.startsWith("kfun:") == true } .filterNot { LLVMIsDeclaration(it) == 1 } .forEach { process(it, currentThreadTLV) } diff --git a/kotlin-native/backend.native/tests/build.gradle b/kotlin-native/backend.native/tests/build.gradle index ab0a83127c9..5b9a16a07d0 100644 --- a/kotlin-native/backend.native/tests/build.gradle +++ b/kotlin-native/backend.native/tests/build.gradle @@ -5581,6 +5581,19 @@ standaloneTest("fake_override_0") { useGoldenData = true } +standaloneTest("split_compilation_pipeline") { + def dir = buildDir.absolutePath + source = "link/private_fake_overrides/override_main.kt" + doBeforeBuild { + konanc("$projectDir/link/private_fake_overrides/override_lib.kt -p library -target ${target.name} -o $dir/lib") + konanc("$projectDir/$source -target ${target.name} -o $dir/out -r $dir -l lib " + + "-Xtemporary-files-dir=$dir/tmp/split " + + "-Xwrite-dependencies-to=${dir}/split_compilation_pipeline.deps") + } + flags = ["-Xread-dependencies-from=${dir}/split_compilation_pipeline.deps", "-Xcompile-from-bitcode=${dir}/tmp/split/out.bc"] + useGoldenData = true +} + linkTest("private_fake_overrides_0") { source = "link/private_fake_overrides/inherit_main.kt" lib = "link/private_fake_overrides/inherit_lib.kt"