diff --git a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt index 21f2562b85a..51a9defaabf 100644 --- a/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt +++ b/backend.native/cli.bc/src/org/jetbrains/kotlin/cli/bc/K2Native.kt @@ -289,13 +289,11 @@ private fun selectIncludes( outputKind: CompilerOutputKind ): List { val includes = arguments.includes?.toList().orEmpty() - val produceBinaryOrBitcode = outputKind.let { it.isNativeBinary || it == CompilerOutputKind.BITCODE } - return if (includes.isNotEmpty() && !produceBinaryOrBitcode) { + return if (includes.isNotEmpty() && outputKind == CompilerOutputKind.LIBRARY) { configuration.report( ERROR, - "The $INCLUDE_ARG flag is only supported when producing native binaries or bitcode files, " + - "but the compiler is producing ${outputKind.name.toLowerCase()}" + "The $INCLUDE_ARG flag is not supported when producing ${outputKind.name.toLowerCase()}" ) emptyList() } else { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt index a7f8d55f72b..1c28abd8a41 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Boxing.kt @@ -150,7 +150,7 @@ internal val Context.getUnboxFunction: (IrClass) -> IrSimpleFunction by Context. * If output target is native binary then the cache is created. */ internal fun initializeCachedBoxes(context: Context) { - if (context.config.produce.isNativeBinary) { + if (context.producedLlvmModuleContainsStdlib) { BoxCache.values().forEach { cache -> val cacheName = "${cache.name}_CACHE" val rangeStart = "${cache.name}_RANGE_FROM" diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt index 7a9ad2520e6..f06ec0582a5 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/CompilerOutput.kt @@ -16,12 +16,28 @@ import org.jetbrains.kotlin.library.* import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.Family -val CompilerOutputKind.isNativeBinary: Boolean get() = when (this) { +/** + * Supposed to be true for a single LLVM module within final binary. + */ +val CompilerOutputKind.isFinalBinary: Boolean get() = when (this) { CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC, CompilerOutputKind.STATIC, CompilerOutputKind.FRAMEWORK -> true CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false } +val CompilerOutputKind.involvesBitcodeGeneration: Boolean + get() = this != CompilerOutputKind.LIBRARY + +internal val Context.producedLlvmModuleContainsStdlib: Boolean + get() = this.llvmModuleSpecification.containsModule(this.stdlibModule) + +val CompilerOutputKind.involvesLinkStage: Boolean + get() = when (this) { + CompilerOutputKind.PROGRAM, CompilerOutputKind.DYNAMIC, + CompilerOutputKind.STATIC, CompilerOutputKind.FRAMEWORK -> true + CompilerOutputKind.LIBRARY, CompilerOutputKind.BITCODE -> false + } + internal fun produceCStubs(context: Context) { val llvmModule = context.llvmModule!! context.cStubsManager.compile(context.config.clang, context.messageCollector, context.inVerbosePhase)?.let { @@ -29,9 +45,12 @@ internal fun produceCStubs(context: Context) { } } -private fun linkAllDependecies(context: Context, generatedBitcodeFiles: List) { - - val nativeLibraries = context.config.nativeLibraries + context.config.defaultNativeLibraries +private fun linkAllDependencies(context: Context, generatedBitcodeFiles: List) { + val runtimeNativeLibraries = context.config.runtimeNativeLibraries + .takeIf { context.producedLlvmModuleContainsStdlib }.orEmpty() + val launcherNativeLibraries = context.config.launcherNativeLibraries + .takeIf { context.config.produce == CompilerOutputKind.PROGRAM }.orEmpty() + val nativeLibraries = context.config.nativeLibraries + runtimeNativeLibraries + launcherNativeLibraries val bitcodeLibraries = context.llvm.bitcodeToLink.map { it.bitcodePaths }.flatten().filter { it.isBitcode } val additionalBitcodeFilesToLink = context.llvm.additionalProducedBitcodeFiles val bitcodeFiles = (nativeLibraries + generatedBitcodeFiles + additionalBitcodeFilesToLink + bitcodeLibraries).toSet() @@ -89,7 +108,7 @@ internal fun produceOutput(context: Context) { if (produce == CompilerOutputKind.FRAMEWORK && context.config.produceStaticFramework) { embedAppleLinkerOptionsToBitcode(context.llvm, context.config) } - linkAllDependecies(context, generatedBitcodeFiles) + linkAllDependencies(context, generatedBitcodeFiles) runLlvmPipeline(context) // Insert `_main` after pipeline so we won't worry about optimizations // corrupting entry point. diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt index 5d9003b4763..20754f6c8cf 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/Context.kt @@ -51,6 +51,7 @@ import org.jetbrains.kotlin.backend.common.serialization.KotlinMangler import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.ir.symbols.impl.IrTypeParameterSymbolImpl +import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.konan.library.KonanLibraryLayout import org.jetbrains.kotlin.library.SerializedIrModule @@ -460,6 +461,15 @@ internal class Context(config: KonanConfig) : KonanBackendContext(config) { get() = this.builtIns.any.module lateinit var compilerOutput: List + + val llvmModuleSpecification: LlvmModuleSpecification = object : LlvmModuleSpecification { + // Currently all code is compiled to single LLVM module. + override fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean = false + override fun containsLibrary(library: KonanLibrary): Boolean = true + override fun containsModule(module: ModuleDescriptor): Boolean = true + override fun containsModule(module: IrModuleFragment): Boolean = true + override fun containsDeclaration(declaration: IrDeclaration): Boolean = true + } } private fun MemberScope.getContributedClassifier(name: String) = diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt index 0367cd2dd37..ff26f1cb654 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/KonanConfig.kt @@ -151,16 +151,17 @@ class KonanConfig(val project: Project, val configuration: CompilerConfiguration return resolvedLibraries.filterRoots { (!it.isDefault && !this.purgeUserLibs) || it.isNeededForLink }.getFullList(TopologicalLibraryOrder) } - internal val defaultNativeLibraries: List = mutableListOf().apply { + internal val runtimeNativeLibraries: List = mutableListOf().apply { add(if (debug) "debug.bc" else "release.bc") add(if (memoryModel == MemoryModel.STRICT) "strict.bc" else "relaxed.bc") - if (produce == CompilerOutputKind.PROGRAM) { - addAll(distribution.launcherFiles) - } }.map { File(distribution.defaultNatives(target)).child(it).absolutePath } + internal val launcherNativeLibraries: List = distribution.launcherFiles.map { + File(distribution.defaultNatives(target)).child(it).absolutePath + } + internal val nativeLibraries: List = configuration.getList(KonanConfigKeys.NATIVE_LIBRARY_FILES) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt new file mode 100644 index 00000000000..db88aa4a558 --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/LlvmModuleSpecification.kt @@ -0,0 +1,22 @@ +/* + * Copyright 2010-2019 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 + +import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.ir.declarations.IrDeclaration +import org.jetbrains.kotlin.ir.declarations.IrModuleFragment +import org.jetbrains.kotlin.konan.library.KonanLibrary + +/** + * Defines what LLVM module should consist of. + */ +interface LlvmModuleSpecification { + fun importsKotlinDeclarationsFromOtherObjectFiles(): Boolean + fun containsLibrary(library: KonanLibrary): Boolean + fun containsModule(module: ModuleDescriptor): Boolean + fun containsModule(module: IrModuleFragment): Boolean + fun containsDeclaration(declaration: IrDeclaration): Boolean +} diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt index c2d9e3f2fd0..35e3ec5b832 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ToplevelPhases.kt @@ -419,7 +419,7 @@ internal fun PhaseConfig.konanPhasesConfig(config: KonanConfig) { disableIf(dependenciesLowerPhase, config.produce == CompilerOutputKind.LIBRARY) disableUnless(entryPointPhase, config.produce == CompilerOutputKind.PROGRAM) disableIf(bitcodePhase, config.produce == CompilerOutputKind.LIBRARY) - disableUnless(linkPhase, config.produce.isNativeBinary) + disableUnless(linkPhase, config.produce.involvesLinkStage) disableIf(testProcessorPhase, getNotNull(KonanConfigKeys.GENERATE_TEST_RUNNER) == TestRunnerKind.NONE) disableUnless(buildDFGPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) disableUnless(devirtualizationPhase, getBoolean(KonanConfigKeys.OPTIMIZATION)) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt index 06cd7562112..52a7776e788 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/ContextUtils.kt @@ -150,7 +150,7 @@ internal interface ContextUtils : RuntimeAware { * or just drop all [else] branches of corresponding conditionals. */ fun isExternal(declaration: IrDeclaration): Boolean { - return false + return !context.llvmModuleSpecification.containsDeclaration(declaration) } /** @@ -399,6 +399,8 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) { context.config.resolvedLibraries .getFullList(TopologicalLibraryOrder) .filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) } + // TODO: the filter above is incorrect when compiling to multiple LLVM modules. + .filter { context.llvmModuleSpecification.containsLibrary(it) } } val additionalProducedBitcodeFiles = mutableListOf() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 811dc419b52..0768f0e1019 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -2246,7 +2246,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map) { - if (context.config.produce.isNativeBinary) { + if (context.config.produce.isFinalBinary) { // Generate function calling all [ctorFunctions]. val globalCtorFunction = LLVMAddFunction(context.llvmModule, "_Konan_constructors", kVoidFuncType)!! LLVMSetLinkage(globalCtorFunction, LLVMLinkage.LLVMPrivateLinkage) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt index 916e0920b50..ab268fad700 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/coverage/CoverageManager.kt @@ -50,7 +50,7 @@ internal class CoverageManager(val context: Context) { } private fun checkRestrictions(): Boolean { - val isKindAllowed = with(context.config.produce) { isNativeBinary || this == CompilerOutputKind.BITCODE } + val isKindAllowed = context.config.produce.involvesBitcodeGeneration val target = context.config.target val isTargetAllowed = target == KonanTarget.MACOS_X64 || target == KonanTarget.IOS_X64 return isKindAllowed && isTargetAllowed diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt index 7f444eb0935..23cfedcb039 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/objcexport/ObjCExportCodeGenerator.kt @@ -262,8 +262,8 @@ internal class ObjCExportCodeGenerator( val sortedAdaptersPointer = staticData.placeGlobalConstArray("", type, sortedAdapters) // Note: this globals replace runtime globals with weak linkage: - staticData.placeGlobal(prefix, sortedAdaptersPointer, isExported = true) - staticData.placeGlobal("${prefix}Num", Int32(sortedAdapters.size), isExported = true) + replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer) + replaceExternalWeakOrCommonGlobal("${prefix}Num", Int32(sortedAdapters.size)) } } @@ -381,6 +381,16 @@ internal class ObjCExportCodeGenerator( } +private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal(name: String, value: ConstValue) { + val global = staticData.placeGlobal(name, value, isExported = true) + + if (context.llvmModuleSpecification.importsKotlinDeclarationsFromOtherObjectFiles()) { + // Note: actually this is required only if global's weak/common definition is in other object file, + // but it is simpler to do this for all globals, considering that all usages can't be removed by DCE anyway. + context.llvm.usedGlobals += global.llvmGlobal + } +} + private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( irClass: IrClass, converter: ConstPointer? = null, @@ -400,20 +410,14 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo( val writableTypeInfoType = runtime.writableTypeInfoType!! val writableTypeInfoValue = Struct(writableTypeInfoType, objCExportAddition) - val global = if (codegen.isExternal(irClass)) { + if (codegen.isExternal(irClass)) { // Note: this global replaces the external one with common linkage. - staticData.createGlobal( - writableTypeInfoType, - irClass.writableTypeInfoSymbolName, - isExported = true - ) + replaceExternalWeakOrCommonGlobal(irClass.writableTypeInfoSymbolName, writableTypeInfoValue) } else { context.llvmDeclarations.forClass(irClass).writableTypeInfoGlobal!!.also { it.setLinkage(LLVMLinkage.LLVMExternalLinkage) - } + }.setInitializer(writableTypeInfoValue) } - - global.setInitializer(writableTypeInfoValue) } private val ObjCExportCodeGenerator.kotlinToObjCFunctionType: LLVMTypeRef @@ -480,7 +484,7 @@ private fun ObjCExportCodeGenerator.emitBlockToKotlinFunctionConverters() { ).pointer.getElementPtr(0) // Note: this global replaces the weak global defined in runtime. - staticData.placeGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr, isExported = true) + replaceExternalWeakOrCommonGlobal("Kotlin_ObjCExport_blockToFunctionConverters", ptr) } private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt index 103f1077cf0..45c7d9c1a32 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/objcexport/ObjCExport.kt @@ -42,7 +42,11 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { private fun produceInterface(): ObjCExportedInterface? { if (!target.family.isAppleFamily) return null - if (!context.config.produce.isNativeBinary) return null // TODO: emit RTTI to the same modules as classes belong to. + if (!context.config.produce.isFinalBinary) return null + + // TODO: emit RTTI to the same modules as classes belong to. + // Not possible yet, since ObjCExport translates the entire "world" API at once + // and can't do this per-module, e.g. due to global name conflict resolution. val produceFramework = context.config.produce == CompilerOutputKind.FRAMEWORK @@ -69,7 +73,7 @@ internal class ObjCExport(val context: Context, symbolTable: SymbolTable) { internal fun generate(codegen: CodeGenerator) { if (!target.family.isAppleFamily) return - if (!context.config.produce.isNativeBinary) return // TODO: emit RTTI to the same modules as classes belong to. + if (!context.config.produce.isFinalBinary) return // TODO: emit RTTI to the same modules as classes belong to. val mapper = exportedInterface?.mapper ?: ObjCExportMapper() val namer = exportedInterface?.namer ?: ObjCExportNamerImpl(