Improve compilation to multiple LLVM modules/object files (#3334)
This commit is contained in:
committed by
GitHub
parent
a9164d858c
commit
d4a98168c0
@@ -289,13 +289,11 @@ private fun selectIncludes(
|
||||
outputKind: CompilerOutputKind
|
||||
): List<String> {
|
||||
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 {
|
||||
|
||||
+1
-1
@@ -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"
|
||||
|
||||
+24
-5
@@ -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<String>) {
|
||||
|
||||
val nativeLibraries = context.config.nativeLibraries + context.config.defaultNativeLibraries
|
||||
private fun linkAllDependencies(context: Context, generatedBitcodeFiles: List<String>) {
|
||||
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.
|
||||
|
||||
+10
@@ -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<ObjectFile>
|
||||
|
||||
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) =
|
||||
|
||||
+5
-4
@@ -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<String> = mutableListOf<String>().apply {
|
||||
internal val runtimeNativeLibraries: List<String> = mutableListOf<String>().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<String> = distribution.launcherFiles.map {
|
||||
File(distribution.defaultNatives(target)).child(it).absolutePath
|
||||
}
|
||||
|
||||
internal val nativeLibraries: List<String> =
|
||||
configuration.getList(KonanConfigKeys.NATIVE_LIBRARY_FILES)
|
||||
|
||||
|
||||
+22
@@ -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
|
||||
}
|
||||
+1
-1
@@ -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))
|
||||
|
||||
+3
-1
@@ -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<String>()
|
||||
|
||||
+10
-3
@@ -2246,7 +2246,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
|
||||
private fun appendDebugSelector() {
|
||||
if (!context.config.produce.isNativeBinary) return
|
||||
if (!context.producedLlvmModuleContainsStdlib) return
|
||||
val llvmDebugSelector =
|
||||
context.llvm.staticData.placeGlobal("KonanNeedDebugInfo",
|
||||
Int32(if (context.shouldContainDebugInfo()) 1 else 0))
|
||||
@@ -2304,7 +2304,14 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
LLVMSetLinkage(ctorFunction, LLVMLinkage.LLVMExternalLinkage)
|
||||
|
||||
val initializers = libraryToInitializers.getValue(library)
|
||||
appendStaticInitializers(ctorFunction, initializers)
|
||||
|
||||
if (library == null || context.llvmModuleSpecification.containsLibrary(library)) {
|
||||
appendStaticInitializers(ctorFunction, initializers)
|
||||
} else {
|
||||
check(initializers.isEmpty()) {
|
||||
"found initializer from ${library.libraryFile}, which is not included into compilation"
|
||||
}
|
||||
}
|
||||
|
||||
ctorFunction
|
||||
}
|
||||
@@ -2342,7 +2349,7 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
}
|
||||
|
||||
private fun appendGlobalCtors(ctorFunctions: List<LLVMValueRef>) {
|
||||
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)
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+16
-12
@@ -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() {
|
||||
|
||||
+6
-2
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user