[K/N][IR][codegen] Build per-file dependencies for caches

This commit is contained in:
Igor Chevdar
2022-10-04 20:08:44 +03:00
committed by Space Team
parent a058fcb628
commit c88ac7cc86
30 changed files with 620 additions and 309 deletions
@@ -35,3 +35,15 @@ val ModuleDescriptor.klibModuleOrigin get() = this.getCapability(KlibModuleOrigi
val ModuleDescriptor.kotlinLibrary get() = val ModuleDescriptor.kotlinLibrary get() =
(this.klibModuleOrigin as DeserializedKlibModuleOrigin) (this.klibModuleOrigin as DeserializedKlibModuleOrigin)
.library .library
sealed class CompiledKlibFileOrigin {
object CurrentFile : CompiledKlibFileOrigin() // No dependency should be added.
object StdlibRuntime : CompiledKlibFileOrigin()
object StdlibKFunctionImpl : CompiledKlibFileOrigin()
class EntireModule(val library: KotlinLibrary) : CompiledKlibFileOrigin()
class CertainFile(val library: KotlinLibrary, val fqName: String, val filePath: String) : CompiledKlibFileOrigin()
}
@@ -69,6 +69,7 @@ internal fun Context.getBoxFunction(inlinedClass: IrClass): IrSimpleFunction = m
returnType = boxedType returnType = boxedType
}.also { function -> }.also { function ->
function.parent = parent function.parent = parent
function.attributeOwnerId = inlinedClass // To be able to get the file.
function.addValueParameter { function.addValueParameter {
startOffset = inlinedClass.startOffset startOffset = inlinedClass.startOffset
@@ -103,6 +104,7 @@ internal fun Context.getUnboxFunction(inlinedClass: IrClass): IrSimpleFunction =
returnType = unboxedType returnType = unboxedType
}.also { function -> }.also { function ->
function.parent = parent function.parent = parent
function.attributeOwnerId = inlinedClass // To be able to get the file.
function.addValueParameter { function.addValueParameter {
startOffset = inlinedClass.startOffset startOffset = inlinedClass.startOffset
@@ -125,7 +125,7 @@ class CacheSupport(
val libraryToAddToCacheFile = File(it) val libraryToAddToCacheFile = File(it)
val libraryToAddToCache = getLibrary(libraryToAddToCacheFile) val libraryToAddToCache = getLibrary(libraryToAddToCacheFile)
val libraryCache = cachedLibraries.getLibraryCache(libraryToAddToCache) val libraryCache = cachedLibraries.getLibraryCache(libraryToAddToCache)
if (libraryCache != null && libraryCache.granularity == CachedLibraries.Granularity.MODULE) if (libraryCache is CachedLibraries.Cache.Monolithic)
null null
else { else {
val filesToCache = configuration.get(KonanConfigKeys.FILES_TO_CACHE) val filesToCache = configuration.get(KonanConfigKeys.FILES_TO_CACHE)
@@ -183,7 +183,7 @@ class CacheSupport(
// Ensure not making cache for libraries that are already cached: // Ensure not making cache for libraries that are already cached:
libraryToCache?.klib?.let { libraryToCache?.klib?.let {
val cache = cachedLibraries.getLibraryCache(it) val cache = cachedLibraries.getLibraryCache(it)
if (cache != null && cache.granularity == CachedLibraries.Granularity.MODULE) { if (cache is CachedLibraries.Cache.Monolithic) {
configuration.reportCompilationError("can't cache library '${it.libraryName}' " + configuration.reportCompilationError("can't cache library '${it.libraryName}' " +
"that is already cached in '${cache.path}'") "that is already cached in '${cache.path}'")
} }
@@ -5,16 +5,18 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.serialization.*
import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields
import org.jetbrains.kotlin.backend.konan.serialization.SerializedInlineFunctionReference
import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.uniqueName import org.jetbrains.kotlin.library.uniqueName
private fun getArtifactName(target: KonanTarget, baseName: String, kind: CompilerOutputKind) =
"${kind.prefix(target)}$baseName${kind.suffix(target)}"
class CachedLibraries( class CachedLibraries(
private val target: KonanTarget, private val target: KonanTarget,
allLibraries: List<KotlinLibrary>, allLibraries: List<KotlinLibrary>,
@@ -22,68 +24,117 @@ class CachedLibraries(
implicitCacheDirectories: List<File> implicitCacheDirectories: List<File>
) { ) {
enum class Kind { DYNAMIC, STATIC } enum class Kind { DYNAMIC, STATIC }
enum class Granularity { MODULE, FILE }
private fun Kind.toCompilerOutputKind(): CompilerOutputKind = when (this) { sealed class BitcodeDependency(val libName: String) {
class WholeModule(libName: String) : BitcodeDependency(libName)
class CertainFiles(libName: String, val files: List<String>) : BitcodeDependency(libName)
}
sealed class Cache(protected val target: KonanTarget, val kind: Kind, val path: String) {
val bitcodeDependencies by lazy { computeBitcodeDependencies() }
val binariesPaths by lazy { computeBinariesPaths() }
val serializedInlineFunctionBodies by lazy { computeSerializedInlineFunctionBodies() }
val serializedClassFields by lazy { computeSerializedClassFields() }
val serializedEagerInitializedFiles by lazy { computeSerializedEagerInitializedFiles() }
protected abstract fun computeBitcodeDependencies(): List<BitcodeDependency>
protected abstract fun computeBinariesPaths(): List<String>
protected abstract fun computeSerializedInlineFunctionBodies(): List<SerializedInlineFunctionReference>
protected abstract fun computeSerializedClassFields(): List<SerializedClassFields>
protected abstract fun computeSerializedEagerInitializedFiles(): List<SerializedEagerInitializedFile>
protected fun Kind.toCompilerOutputKind(): CompilerOutputKind = when (this) {
Kind.DYNAMIC -> CompilerOutputKind.DYNAMIC_CACHE Kind.DYNAMIC -> CompilerOutputKind.DYNAMIC_CACHE
Kind.STATIC -> CompilerOutputKind.STATIC_CACHE Kind.STATIC -> CompilerOutputKind.STATIC_CACHE
} }
inner class Cache(val kind: Kind, val granularity: Granularity, val path: String) { protected fun parseDependencies(dependencies: List<String>): List<BitcodeDependency> {
val fileDirs by lazy { File(path).listFiles.filter { it.isDirectory }.sortedBy { it.name } } val wholeModuleDependencies = mutableListOf<String>()
val fileDependencies = mutableMapOf<String, MutableList<String>>()
val bitcodeDependencies by lazy { for (dependency in dependencies) {
when (granularity) { val delimiterIndex = dependency.lastIndexOf(DEPENDENCIES_DELIMITER)
Granularity.MODULE -> File(path).parentFile.child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings() require(delimiterIndex >= 0) { "Invalid dependency $dependency of library $path" }
Granularity.FILE -> fileDirs.flatMap { val libName = dependency.substring(0, delimiterIndex)
it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings() val file = dependency.substring(delimiterIndex + 1, dependency.length)
}.distinct() if (file.isEmpty())
wholeModuleDependencies.add(libName)
else
fileDependencies.getOrPut(libName) { mutableListOf() }.add(file)
} }
return wholeModuleDependencies.map { BitcodeDependency.WholeModule(it) } +
fileDependencies.map { (libName, files) -> BitcodeDependency.CertainFiles(libName, files) }
} }
val binariesPaths by lazy { class Monolithic(target: KonanTarget, kind: Kind, path: String) : Cache(target, kind, path) {
when (granularity) { override fun computeBitcodeDependencies() =
Granularity.MODULE -> listOf(path) parseDependencies(File(path).parentFile.child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings())
Granularity.FILE -> fileDirs.map {
it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(it.name, kind.toCompilerOutputKind())).absolutePath
}
}
}
val serializedInlineFunctionBodies by lazy { override fun computeBinariesPaths() = listOf(path)
val result = mutableListOf<SerializedInlineFunctionReference>()
when (granularity) { override fun computeSerializedInlineFunctionBodies() = mutableListOf<SerializedInlineFunctionReference>().also {
Granularity.MODULE -> {
val directory = File(path).absoluteFile.parentFile.parentFile val directory = File(path).absoluteFile.parentFile.parentFile
val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes() val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes()
InlineFunctionBodyReferenceSerializer.deserializeTo(data, result) InlineFunctionBodyReferenceSerializer.deserializeTo(data, it)
}
Granularity.FILE -> {
fileDirs.forEach { fileDir ->
val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes()
InlineFunctionBodyReferenceSerializer.deserializeTo(data, result)
}
}
}
result
} }
val serializedClassFields by lazy { override fun computeSerializedClassFields() = mutableListOf<SerializedClassFields>().also {
val result = mutableListOf<SerializedClassFields>()
when (granularity) {
Granularity.MODULE -> {
val directory = File(path).absoluteFile.parentFile.parentFile val directory = File(path).absoluteFile.parentFile.parentFile
val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes()
ClassFieldsSerializer.deserializeTo(data, result) ClassFieldsSerializer.deserializeTo(data, it)
} }
Granularity.FILE -> {
override fun computeSerializedEagerInitializedFiles() = mutableListOf<SerializedEagerInitializedFile>().also {
val directory = File(path).absoluteFile.parentFile.parentFile
val data = directory.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(EAGER_INITIALIZED_PROPERTIES_FILE_NAME).readBytes()
EagerInitializedPropertySerializer.deserializeTo(data, it)
}
}
class PerFile(target: KonanTarget, kind: Kind, path: String) : Cache(target, kind, path) {
private val fileDirs by lazy { File(path).listFiles.filter { it.isDirectory }.sortedBy { it.name } }
private val perFileBitcodeDependencies by lazy {
fileDirs.associate {
it.name to parseDependencies(it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(BITCODE_DEPENDENCIES_FILE_NAME).readStrings())
}
}
fun getFileDependencies(file: String) =
perFileBitcodeDependencies[file] ?: error("File $file is not found in cache $path")
fun getFileBinaryPath(file: String) =
File(path).child(file).child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(target, file, kind.toCompilerOutputKind())).let {
require(it.exists) { "File $file is not found in cache $path" }
it.absolutePath
}
override fun computeBitcodeDependencies() = perFileBitcodeDependencies.values.flatten()
override fun computeBinariesPaths() = fileDirs.map {
it.child(PER_FILE_CACHE_BINARY_LEVEL_DIR_NAME).child(getArtifactName(target, it.name, kind.toCompilerOutputKind())).absolutePath
}
override fun computeSerializedInlineFunctionBodies() = mutableListOf<SerializedInlineFunctionReference>().also {
fileDirs.forEach { fileDir ->
val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(INLINE_FUNCTION_BODIES_FILE_NAME).readBytes()
InlineFunctionBodyReferenceSerializer.deserializeTo(data, it)
}
}
override fun computeSerializedClassFields() = mutableListOf<SerializedClassFields>().also {
fileDirs.forEach { fileDir -> fileDirs.forEach { fileDir ->
val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes() val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(CLASS_FIELDS_FILE_NAME).readBytes()
ClassFieldsSerializer.deserializeTo(data, result) ClassFieldsSerializer.deserializeTo(data, it)
} }
} }
override fun computeSerializedEagerInitializedFiles() = mutableListOf<SerializedEagerInitializedFile>().also {
fileDirs.forEach { fileDir ->
val data = fileDir.child(PER_FILE_CACHE_IR_LEVEL_DIR_NAME).child(EAGER_INITIALIZED_PROPERTIES_FILE_NAME).readBytes()
EagerInitializedPropertySerializer.deserializeTo(data, it)
}
} }
result
} }
} }
@@ -100,16 +151,16 @@ class CachedLibraries(
cacheBinaryPartDir.listFilesOrEmpty.map { it.absolutePath }.toSet() cacheBinaryPartDir.listFilesOrEmpty.map { it.absolutePath }.toSet()
} }
val baseName = getCachedLibraryName(library) val baseName = getCachedLibraryName(library)
val dynamicFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.DYNAMIC_CACHE)) val dynamicFile = cacheBinaryPartDir.child(getArtifactName(target, baseName, CompilerOutputKind.DYNAMIC_CACHE))
val staticFile = cacheBinaryPartDir.child(getArtifactName(baseName, CompilerOutputKind.STATIC_CACHE)) val staticFile = cacheBinaryPartDir.child(getArtifactName(target, baseName, CompilerOutputKind.STATIC_CACHE))
if (dynamicFile.absolutePath in cacheBinaryPartDirContents && staticFile.absolutePath in cacheBinaryPartDirContents) if (dynamicFile.absolutePath in cacheBinaryPartDirContents && staticFile.absolutePath in cacheBinaryPartDirContents)
error("Both dynamic and static caches files cannot be in the same directory." + error("Both dynamic and static caches files cannot be in the same directory." +
" Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}") " Library: ${library.libraryName}, path to cache: ${cacheDir.absolutePath}")
return when { return when {
dynamicFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.DYNAMIC, Granularity.MODULE, dynamicFile.absolutePath) dynamicFile.absolutePath in cacheBinaryPartDirContents -> Cache.Monolithic(target, Kind.DYNAMIC, dynamicFile.absolutePath)
staticFile.absolutePath in cacheBinaryPartDirContents -> Cache(Kind.STATIC, Granularity.MODULE, staticFile.absolutePath) staticFile.absolutePath in cacheBinaryPartDirContents -> Cache.Monolithic(target, Kind.STATIC, staticFile.absolutePath)
else -> Cache(Kind.STATIC, Granularity.FILE, cacheDir.absolutePath) else -> Cache.PerFile(target, Kind.STATIC, cacheDir.absolutePath)
} }
} }
@@ -129,9 +180,6 @@ class CachedLibraries(
cache?.let { library to it } cache?.let { library to it }
}.toMap() }.toMap()
private fun getArtifactName(baseName: String, kind: CompilerOutputKind) =
"${kind.prefix(target)}$baseName${kind.suffix(target)}"
fun isLibraryCached(library: KotlinLibrary): Boolean = fun isLibraryCached(library: KotlinLibrary): Boolean =
getLibraryCache(library) != null getLibraryCache(library) != null
@@ -163,5 +211,8 @@ class CachedLibraries(
const val BITCODE_DEPENDENCIES_FILE_NAME = "bitcode_deps" const val BITCODE_DEPENDENCIES_FILE_NAME = "bitcode_deps"
const val INLINE_FUNCTION_BODIES_FILE_NAME = "inline_bodies" const val INLINE_FUNCTION_BODIES_FILE_NAME = "inline_bodies"
const val CLASS_FIELDS_FILE_NAME = "class_fields" const val CLASS_FIELDS_FILE_NAME = "class_fields"
const val EAGER_INITIALIZED_PROPERTIES_FILE_NAME = "eager_init"
const val DEPENDENCIES_DELIMITER = '|'
} }
} }
@@ -37,25 +37,22 @@ private class CallsChecker(generationState: NativeGenerationState, goodFunctions
private fun moduleFunction(name: String) = private fun moduleFunction(name: String) =
LLVMGetNamedFunction(llvm.module, name) ?: throw IllegalStateException("$name function is not available") LLVMGetNamedFunction(llvm.module, name) ?: throw IllegalStateException("$name function is not available")
val getMethodImpl = llvm.externalFunction(LlvmFunctionProto( val getMethodImpl = llvm.externalNativeRuntimeFunction(
"class_getMethodImplementation", "class_getMethodImplementation",
LlvmRetType(pointerType(functionType(llvm.voidType, false))), LlvmRetType(pointerType(functionType(llvm.voidType, false))),
listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType))
origin = context.stdlibModule.llvmSymbolOrigin)
) )
val getClass = llvm.externalFunction(LlvmFunctionProto( val getClass = llvm.externalNativeRuntimeFunction(
"object_getClass", "object_getClass",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = context.stdlibModule.llvmSymbolOrigin)
) )
val getSuperClass = llvm.externalFunction(LlvmFunctionProto( val getSuperClass = llvm.externalNativeRuntimeFunction(
"class_getSuperclass", "class_getSuperclass",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = context.stdlibModule.llvmSymbolOrigin)
) )
val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall") val checkerFunction = moduleFunction("Kotlin_mm_checkStateAtExternalFunctionCall")
@@ -60,7 +60,7 @@ internal val NativeGenerationState.shouldDefineCachedBoxes: Boolean
internal val NativeGenerationState.shouldLinkRuntimeNativeLibraries: Boolean internal val NativeGenerationState.shouldLinkRuntimeNativeLibraries: Boolean
get() = producedLlvmModuleContainsStdlib && get() = producedLlvmModuleContainsStdlib &&
cacheDeserializationStrategy?.contains(KonanFqNames.packageName, "Runtime.kt") != false cacheDeserializationStrategy?.contains(KonanFqNames.internalPackageName, "Runtime.kt") != false
val KonanConfig.involvesLinkStage: Boolean val KonanConfig.involvesLinkStage: Boolean
get() = when (this.produce) { get() = when (this.produce) {
@@ -1,7 +1,9 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import org.jetbrains.kotlin.backend.konan.driver.PhaseContext import org.jetbrains.kotlin.backend.konan.driver.PhaseContext
import org.jetbrains.kotlin.backend.konan.llvm.Llvm
import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer import org.jetbrains.kotlin.backend.konan.serialization.ClassFieldsSerializer
import org.jetbrains.kotlin.backend.konan.serialization.EagerInitializedPropertySerializer
import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer import org.jetbrains.kotlin.backend.konan.serialization.InlineFunctionBodyReferenceSerializer
import org.jetbrains.kotlin.konan.KonanExternalToolFailure import org.jetbrains.kotlin.konan.KonanExternalToolFailure
import org.jetbrains.kotlin.konan.exec.Command import org.jetbrains.kotlin.konan.exec.Command
@@ -55,17 +57,33 @@ internal class CacheStorage(val generationState: NativeGenerationState) {
saveCacheBitcodeDependencies() saveCacheBitcodeDependencies()
saveInlineFunctionBodies() saveInlineFunctionBodies()
saveClassFields() saveClassFields()
saveEagerInitializedProperties()
} }
private fun saveCacheBitcodeDependencies() { private fun saveCacheBitcodeDependencies() {
val bitcodeDependencies = config.resolvedLibraries val libraryToCache = config.cacheSupport.libraryToCache!!
val usedBitcode = generationState.llvmImports.usedBitcode().groupBy { it.library }
val bitcodeModuleDependencies = mutableListOf<String>()
val bitcodeFileDependencies = mutableListOf<String>()
val strategy = libraryToCache.strategy as? CacheDeserializationStrategy.SingleFile
config.resolvedLibraries
.getFullList(TopologicalLibraryOrder) .getFullList(TopologicalLibraryOrder)
.map { it as KonanLibrary } .forEach { library ->
.filter { require(library is KonanLibrary)
generationState.llvmImports.bitcodeIsUsed(it) val filesUsed = usedBitcode[library]
&& it != config.cacheSupport.libraryToCache?.klib // Skip loops. if (filesUsed == null && generationState.llvmImports.bitcodeIsUsed(library)
&& library != libraryToCache.klib /* Skip loops */) {
// Dependency on the entire library.
bitcodeModuleDependencies.add("${library.uniqueName}${CachedLibraries.DEPENDENCIES_DELIMITER}")
} }
outputFiles.bitcodeDependenciesFile!!.writeLines(bitcodeDependencies.map { it.uniqueName }) filesUsed?.forEach { libraryFile ->
if (library != libraryToCache.klib || strategy?.filePath != libraryFile.filePath /* Skip loops */) {
val fileId = CacheSupport.cacheFileId(libraryFile.fqName, libraryFile.filePath)
bitcodeFileDependencies.add("${library.uniqueName}${CachedLibraries.DEPENDENCIES_DELIMITER}$fileId")
}
}
}
outputFiles.bitcodeDependenciesFile!!.writeLines(bitcodeModuleDependencies + bitcodeFileDependencies)
} }
private fun saveInlineFunctionBodies() { private fun saveInlineFunctionBodies() {
@@ -77,6 +95,11 @@ internal class CacheStorage(val generationState: NativeGenerationState) {
outputFiles.classFieldsFile!!.writeBytes( outputFiles.classFieldsFile!!.writeBytes(
ClassFieldsSerializer.serialize(generationState.classFields)) ClassFieldsSerializer.serialize(generationState.classFields))
} }
private fun saveEagerInitializedProperties() {
outputFiles.eagerInitializedPropertiesFile!!.writeBytes(
EagerInitializedPropertySerializer.serialize(generationState.eagerInitializedFiles))
}
} }
// TODO: We have a Linker.kt file in the shared module. // TODO: We have a Linker.kt file in the shared module.
@@ -239,7 +262,8 @@ private fun determineCachesToLink(generationState: NativeGenerationState): Cache
val staticCaches = mutableListOf<String>() val staticCaches = mutableListOf<String>()
val dynamicCaches = mutableListOf<String>() val dynamicCaches = mutableListOf<String>()
generationState.llvm.allCachedBitcodeDependencies.forEach { library -> generationState.llvm.allCachedBitcodeDependencies.forEach { dependency ->
val library = dependency.library
val currentBinaryContainsLibrary = generationState.llvmModuleSpecification.containsLibrary(library) val currentBinaryContainsLibrary = generationState.llvmModuleSpecification.containsLibrary(library)
val cache = generationState.config.cachedLibraries.getLibraryCache(library) val cache = generationState.config.cachedLibraries.getLibraryCache(library)
?: error("Library $library is expected to be cached") ?: error("Library $library is expected to be cached")
@@ -253,7 +277,9 @@ private fun determineCachesToLink(generationState: NativeGenerationState): Cache
CachedLibraries.Kind.STATIC -> staticCaches CachedLibraries.Kind.STATIC -> staticCaches
} }
list += cache.binariesPaths list += if (dependency is Llvm.CachedBitcodeDependency.CertainFiles && cache is CachedLibraries.Cache.PerFile)
dependency.files.map { cache.getFileBinaryPath(it) }
else cache.binariesPaths
} }
return CachesToLink(static = staticCaches, dynamic = dynamicCaches) return CachesToLink(static = staticCaches, dynamic = dynamicCaches)
} }
@@ -6,18 +6,22 @@
package org.jetbrains.kotlin.backend.konan package org.jetbrains.kotlin.backend.konan
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext import org.jetbrains.kotlin.backend.konan.driver.BasicPhaseContext
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager import org.jetbrains.kotlin.backend.konan.llvm.coverage.CoverageManager
import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport import org.jetbrains.kotlin.backend.konan.objcexport.ObjCExport
import org.jetbrains.kotlin.backend.konan.serialization.SerializedClassFields 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.backend.konan.serialization.SerializedInlineFunctionReference
import org.jetbrains.kotlin.ir.declarations.IrAttributeContainer import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.util.getPackageFragment
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.konan.TempFiles import org.jetbrains.kotlin.konan.TempFiles
import org.jetbrains.kotlin.konan.file.File import org.jetbrains.kotlin.konan.file.File
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
internal class InlineFunctionOriginInfo(val irFunction: IrFunction, val irFile: IrFile, val startOffset: Int, val endOffset: Int) internal class InlineFunctionOriginInfo(val irFunction: IrFunction, val irFile: IrFile, val startOffset: Int, val endOffset: Int)
@@ -61,6 +65,7 @@ internal class NativeGenerationState(
val inlineFunctionBodies = mutableListOf<SerializedInlineFunctionReference>() val inlineFunctionBodies = mutableListOf<SerializedInlineFunctionReference>()
val classFields = mutableListOf<SerializedClassFields>() val classFields = mutableListOf<SerializedClassFields>()
val eagerInitializedFiles = mutableListOf<SerializedEagerInitializedFile>()
val calledFromExportedInlineFunctions = mutableSetOf<IrFunction>() val calledFromExportedInlineFunctions = mutableSetOf<IrFunction>()
val constructedFromExportedInlineFunctions = mutableSetOf<IrClass>() val constructedFromExportedInlineFunctions = mutableSetOf<IrClass>()
val loweredInlineFunctions = mutableMapOf<IrFunction, InlineFunctionOriginInfo>() val loweredInlineFunctions = mutableMapOf<IrFunction, InlineFunctionOriginInfo>()
@@ -105,6 +110,25 @@ internal class NativeGenerationState(
lateinit var objCExport: ObjCExport lateinit var objCExport: ObjCExport
fun computeOrigin(declaration: IrDeclaration): CompiledKlibFileOrigin {
val packageFragment = declaration.getPackageFragment()
return if (packageFragment.isFunctionInterfaceFile)
CompiledKlibFileOrigin.StdlibKFunctionImpl
else {
val library = when (val origin = declaration.llvmSymbolOrigin) {
CurrentKlibModuleOrigin -> config.libraryToCache?.klib?.takeIf { config.producePerFileCache }
else -> (origin as DeserializedKlibModuleOrigin).library
}
when {
library == null -> CompiledKlibFileOrigin.CurrentFile
packageFragment.packageFragmentDescriptor.containingDeclaration.isFromInteropLibrary() ->
CompiledKlibFileOrigin.EntireModule(library)
else -> CompiledKlibFileOrigin.CertainFile(library, packageFragment.fqName.asString(),
context.irLinker.getExternalDeclarationFileName(declaration))
}
}
}
fun hasDebugInfo() = debugInfoDelegate.isInitialized() fun hasDebugInfo() = debugInfoDelegate.isInitialized()
fun verifyBitCode() { fun verifyBitCode() {
@@ -73,6 +73,8 @@ class OutputFiles(val outputName: String, target: KonanTarget, val produce: Comp
val classFieldsFile = tempCacheDirectory?.cacheIrPart()?.child(CachedLibraries.CLASS_FIELDS_FILE_NAME) val classFieldsFile = tempCacheDirectory?.cacheIrPart()?.child(CachedLibraries.CLASS_FIELDS_FILE_NAME)
val eagerInitializedPropertiesFile = tempCacheDirectory?.cacheIrPart()?.child(CachedLibraries.EAGER_INITIALIZED_PROPERTIES_FILE_NAME)
private fun String.fullOutputName() = prefixBaseNameIfNeeded(prefix).suffixIfNeeded(suffix) private fun String.fullOutputName() = prefixBaseNameIfNeeded(prefix).suffixIfNeeded(suffix)
private fun String.prefixBaseNameIfNeeded(prefix: String) = private fun String.prefixBaseNameIfNeeded(prefix: String) =
@@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) { internal fun moduleValidationCallback(state: ActionState, module: IrModuleFragment, context: Context) {
@@ -11,11 +11,17 @@ import org.jetbrains.kotlin.backend.konan.driver.PhaseEngine
import org.jetbrains.kotlin.backend.konan.driver.runPhaseInParentContext import org.jetbrains.kotlin.backend.konan.driver.runPhaseInParentContext
import org.jetbrains.kotlin.backend.konan.llvm.linkBitcodeDependenciesPhase import org.jetbrains.kotlin.backend.konan.llvm.linkBitcodeDependenciesPhase
import org.jetbrains.kotlin.backend.konan.llvm.printBitcodePhase import org.jetbrains.kotlin.backend.konan.llvm.printBitcodePhase
import org.jetbrains.kotlin.backend.konan.llvm.standardLlvmSymbolsOrigin
import org.jetbrains.kotlin.backend.konan.llvm.verifyBitcodePhase import org.jetbrains.kotlin.backend.konan.llvm.verifyBitcodePhase
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
import org.jetbrains.kotlin.ir.declarations.path import org.jetbrains.kotlin.ir.declarations.path
import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
internal fun PhaseEngine<PhaseContext>.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? { internal fun PhaseEngine<PhaseContext>.runFrontend(config: KonanConfig, environment: KotlinCoreEnvironment): FrontendPhaseOutput.Full? {
val frontendOutput = useContext(FrontendContextImpl(config)) { it.runFrontend(environment) } val frontendOutput = useContext(FrontendContextImpl(config)) { it.runFrontend(environment) }
@@ -57,6 +63,14 @@ internal fun <C : PhaseContext> PhaseEngine<C>.runBackend(backendContext: Contex
} }
} }
private fun isReferencedByNativeRuntime(declarations: List<IrDeclaration>): Boolean =
declarations.any {
it.hasAnnotation(RuntimeNames.exportTypeInfoAnnotation)
|| it.hasAnnotation(RuntimeNames.exportForCppRuntime)
} || declarations.any {
it is IrClass && isReferencedByNativeRuntime(it.declarations)
}
internal fun PhaseEngine<out Context>.processModuleFragments( internal fun PhaseEngine<out Context>.processModuleFragments(
input: IrModuleFragment, input: IrModuleFragment,
action: (NativeGenerationState, IrModuleFragment) -> Unit action: (NativeGenerationState, IrModuleFragment) -> Unit
@@ -66,21 +80,35 @@ internal fun PhaseEngine<out Context>.processModuleFragments(
val files = module.files.toList() val files = module.files.toList()
module.files.clear() module.files.clear()
val functionInterfaceFiles = files.filter { it.isFunctionInterfaceFile }
val stdlibIsBeingCached = module.descriptor == context.stdlibModule
val functionInterfaceFiles = files.takeIf { stdlibIsBeingCached }
?.filter { it.isFunctionInterfaceFile }.orEmpty()
val filesReferencedByNativeRuntime = files.takeIf { stdlibIsBeingCached }
?.filter { isReferencedByNativeRuntime(it.declarations) }.orEmpty()
for (file in files) { for (file in files) {
if (file.isFunctionInterfaceFile) continue if (file.isFunctionInterfaceFile) continue
context.generationState = NativeGenerationState( val generationState = NativeGenerationState(
context.config, context.config,
context, context,
CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString()) CacheDeserializationStrategy.SingleFile(file.path, file.fqName.asString())
) )
context.generationState = generationState
module.files += file module.files += file
if (context.generationState.shouldDefineFunctionClasses) if (generationState.shouldDefineFunctionClasses)
module.files += functionInterfaceFiles module.files += functionInterfaceFiles
if (generationState.shouldLinkRuntimeNativeLibraries) {
val stdlib = (context.standardLlvmSymbolsOrigin as DeserializedKlibModuleOrigin).library
filesReferencedByNativeRuntime.forEach {
generationState.llvmImports.add(
CompiledKlibFileOrigin.CertainFile(stdlib, it.fqName.asString(), it.path))
}
}
action(context.generationState, input) action(context.generationState, input)
module.files.clear() module.files.clear()
@@ -17,11 +17,11 @@ import org.jetbrains.kotlin.backend.konan.llvm.ThreadState.Runnable
import org.jetbrains.kotlin.backend.konan.llvm.objc.* import org.jetbrains.kotlin.backend.konan.llvm.objc.*
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.ForeignExceptionMode import org.jetbrains.kotlin.konan.ForeignExceptionMode
import org.jetbrains.kotlin.konan.target.CompilerOutputKind import org.jetbrains.kotlin.konan.target.CompilerOutputKind
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
internal class CodeGenerator(override val generationState: NativeGenerationState) : ContextUtils { internal class CodeGenerator(override val generationState: NativeGenerationState) : ContextUtils {
@@ -1265,21 +1265,20 @@ internal abstract class FunctionGenerationContext(
assert(!irClass.isInterface) assert(!irClass.isInterface)
return if (irClass.isExternalObjCClass()) { return if (irClass.isExternalObjCClass()) {
val llvmSymbolOrigin = irClass.llvmSymbolOrigin val origin = generationState.computeOrigin(irClass)
if (irClass.isObjCMetaClass()) { if (irClass.isObjCMetaClass()) {
val name = irClass.descriptor.getExternalObjCMetaClassBinaryName() val name = irClass.descriptor.getExternalObjCMetaClassBinaryName()
val objCClass = getObjCClass(name, llvmSymbolOrigin) val objCClass = getObjCClass(name, origin)
val getClass = llvm.externalFunction(LlvmFunctionProto( val getClass = llvm.externalNativeRuntimeFunction(
"object_getClass", "object_getClass",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = context.standardLlvmSymbolsOrigin )
))
call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler) call(getClass, listOf(objCClass), exceptionHandler = exceptionHandler)
} else { } else {
getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), llvmSymbolOrigin) getObjCClass(irClass.descriptor.getExternalObjCClassBinaryName(), origin)
} }
} else { } else {
if (irClass.isObjCMetaClass()) { if (irClass.isObjCMetaClass()) {
@@ -1303,8 +1302,8 @@ internal abstract class FunctionGenerationContext(
} }
} }
fun getObjCClass(binaryName: String, llvmSymbolOrigin: CompiledKlibModuleOrigin): LLVMValueRef { fun getObjCClass(binaryName: String, origin: CompiledKlibFileOrigin): LLVMValueRef {
llvm.imports.add(llvmSymbolOrigin) llvm.imports.add(origin)
return load(codegen.objCDataGenerator!!.genClassRef(binaryName).llvm) return load(codegen.objCDataGenerator!!.genClassRef(binaryName).llvm)
} }
@@ -1467,17 +1466,15 @@ internal abstract class FunctionGenerationContext(
} }
private val kotlinExceptionRtti: ConstPointer private val kotlinExceptionRtti: ConstPointer
get() = constPointer(importGlobal( get() = constPointer(importNativeRuntimeGlobal(
"_ZTI18ExceptionObjHolder", // typeinfo for ObjHolder "_ZTI18ExceptionObjHolder", // typeinfo for ObjHolder
llvm.int8PtrType, llvm.int8PtrType
origin = context.stdlibModule.llvmSymbolOrigin
)).bitcast(llvm.int8PtrType) )).bitcast(llvm.int8PtrType)
private val objcNSExceptionRtti: ConstPointer by lazy { private val objcNSExceptionRtti: ConstPointer by lazy {
constPointer(importGlobal( constPointer(importNativeRuntimeGlobal(
"OBJC_EHTYPE_\$_NSException", // typeinfo for NSException* "OBJC_EHTYPE_\$_NSException", // typeinfo for NSException*
llvm.int8PtrType, llvm.int8PtrType
origin = context.stdlibModule.llvmSymbolOrigin
)).bitcast(llvm.int8PtrType) )).bitcast(llvm.int8PtrType)
} }
@@ -8,18 +8,24 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.toCValues import kotlinx.cinterop.toCValues
import kotlinx.cinterop.toKString import kotlinx.cinterop.toKString
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary
import org.jetbrains.kotlin.library.metadata.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.descriptors.konan.*
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.konan.target.KonanTarget import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.resolver.TopologicalLibraryOrder
import org.jetbrains.kotlin.library.uniqueName import org.jetbrains.kotlin.library.uniqueName
import org.jetbrains.kotlin.name.FqName
import kotlin.properties.ReadOnlyProperty import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
@@ -215,8 +221,7 @@ internal interface ContextUtils : RuntimeAware {
this.computePrivateTypeInfoSymbolName(context.irLinker.getExternalDeclarationFileName(this)) this.computePrivateTypeInfoSymbolName(context.irLinker.getExternalDeclarationFileName(this))
} }
constPointer(importGlobal(typeInfoSymbolName, runtime.typeInfoType, constPointer(importGlobal(typeInfoSymbolName, runtime.typeInfoType, generationState.computeOrigin(this)))
origin = this.llvmSymbolOrigin))
} else { } else {
generationState.llvmDeclarations.forClass(this).typeInfo generationState.llvmDeclarations.forClass(this).typeInfo
} }
@@ -359,6 +364,21 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu
} }
} }
internal fun externalNativeRuntimeFunction(
name: String,
returnType: LlvmRetType,
parameterTypes: List<LlvmParamType> = emptyList(),
functionAttributes: List<LlvmFunctionAttribute> = emptyList(),
isVararg: Boolean = false
) = externalFunction(
LlvmFunctionProto(name, returnType, parameterTypes, functionAttributes,
origin = CompiledKlibFileOrigin.StdlibRuntime,
isVararg, independent = false)
)
internal fun externalNativeRuntimeFunction(name: String, signature: LlvmFunctionSignature) =
externalNativeRuntimeFunction(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, signature.isVararg)
val imports get() = generationState.llvmImports val imports get() = generationState.llvmImports
class ImportsImpl(private val context: Context) : LlvmImports { class ImportsImpl(private val context: Context) : LlvmImports {
@@ -366,27 +386,48 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu
private val usedBitcode = mutableSetOf<KotlinLibrary>() private val usedBitcode = mutableSetOf<KotlinLibrary>()
private val usedNativeDependencies = mutableSetOf<KotlinLibrary>() private val usedNativeDependencies = mutableSetOf<KotlinLibrary>()
private val usedBitcodeOfFile = mutableSetOf<LlvmImports.LibraryFile>()
private val allLibraries by lazy { context.librariesWithDependencies.toSet() } private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
override fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean) { private fun findStdlibFile(fqName: FqName, fileName: String): LlvmImports.LibraryFile {
val library = when (origin) { val stdlib = (context.standardLlvmSymbolsOrigin as? DeserializedKlibModuleOrigin)?.library
CurrentKlibModuleOrigin -> return ?: error("Can't find stdlib")
is DeserializedKlibModuleOrigin -> origin.library val stdlibDeserializer = context.irLinker.moduleDeserializers[context.stdlibModule]
?: error("No deserializer for stdlib")
val file = stdlibDeserializer.files.atMostOne { it.fqName == fqName && it.name == fileName }
?: error("Can't find $fqName:$fileName in stdlib")
return LlvmImports.LibraryFile(stdlib, file.fqName.asString(), file.path)
} }
if (library !in allLibraries) { private val stdlibRuntime by lazy { findStdlibFile(KonanFqNames.internalPackageName, "Runtime.kt") }
error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}") private val stdlibKFunctionImpl by lazy { findStdlibFile(KonanFqNames.internalPackageName, "KFunctionImpl.kt") }
override fun add(origin: CompiledKlibFileOrigin, onlyBitcode: Boolean) {
val libraryFile = when (origin) {
CompiledKlibFileOrigin.CurrentFile -> return
is CompiledKlibFileOrigin.EntireModule -> null
is CompiledKlibFileOrigin.CertainFile -> LlvmImports.LibraryFile(origin.library, origin.fqName, origin.filePath)
CompiledKlibFileOrigin.StdlibRuntime -> stdlibRuntime
CompiledKlibFileOrigin.StdlibKFunctionImpl -> stdlibKFunctionImpl
} }
val library = libraryFile?.library ?: (origin as CompiledKlibFileOrigin.EntireModule).library
if (library !in allLibraries)
error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}")
usedBitcode.add(library) usedBitcode.add(library)
if (!onlyBitcode) { if (!onlyBitcode) {
usedNativeDependencies.add(library) usedNativeDependencies.add(library)
} }
libraryFile?.let { usedBitcodeOfFile.add(it) }
} }
override fun bitcodeIsUsed(library: KonanLibrary) = library in usedBitcode override fun bitcodeIsUsed(library: KonanLibrary) = library in usedBitcode
override fun nativeDependenciesAreUsed(library: KonanLibrary) = library in usedNativeDependencies override fun nativeDependenciesAreUsed(library: KonanLibrary) = library in usedNativeDependencies
override fun usedBitcode(): List<LlvmImports.LibraryFile> = usedBitcodeOfFile.toList()
} }
val nativeDependenciesToLink: List<KonanLibrary> by lazy { val nativeDependenciesToLink: List<KonanLibrary> by lazy {
@@ -403,47 +444,117 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu
.filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) } .filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) }
} }
val allCachedBitcodeDependencies: List<KonanLibrary> by lazy { sealed class CachedBitcodeDependency(val library: KonanLibrary) {
val allLibraries = context.config.resolvedLibraries.getFullList().associateBy { it.uniqueName } class WholeModule(library: KonanLibrary) : CachedBitcodeDependency(library)
val result = mutableSetOf<KonanLibrary>()
fun addDependencies(cachedLibrary: CachedLibraries.Cache) { class CertainFiles(library: KonanLibrary, val files: List<String>) : CachedBitcodeDependency(library)
cachedLibrary.bitcodeDependencies.forEach {
val library = allLibraries[it] ?: error("Bitcode dependency to an unknown library: $it")
result.add(library as KonanLibrary)
addDependencies(context.config.cachedLibraries.getLibraryCache(library)
?: error("Library $it is expected to be cached"))
}
} }
private inner class CachedBitcodeDependenciesComputer {
private val allLibraries = context.config.resolvedLibraries.getFullList().associateBy { it.uniqueName }
private val usedBitcode = imports.usedBitcode().groupBy { it.library }
private val moduleDependencies = mutableSetOf<KonanLibrary>()
private val fileDependencies = mutableMapOf<KonanLibrary, MutableSet<String>>()
val allDependencies: List<CachedBitcodeDependency>
init {
val moduleDeserializers = context.irLinker.moduleDeserializers.values.associateBy { it.klib }
for (library in immediateBitcodeDependencies) { for (library in immediateBitcodeDependencies) {
if (library == context.config.libraryToCache?.klib) continue if (library == context.config.libraryToCache?.klib) continue
val cache = context.config.cachedLibraries.getLibraryCache(library) val cache = context.config.cachedLibraries.getLibraryCache(library)
if (cache != null) { if (cache != null) {
result += library val filesUsed = buildList {
addDependencies(cache) usedBitcode[library]?.forEach {
add(CacheSupport.cacheFileId(it.fqName, it.filePath))
}
val moduleDeserializer = moduleDeserializers[library]
if (moduleDeserializer == null) {
require(library.isInteropLibrary()) { "No module deserializer for cached library ${library.uniqueName}" }
} else {
moduleDeserializer.eagerInitializedFiles.forEach {
add(CacheSupport.cacheFileId(it.fqName.asString(), it.path))
}
} }
} }
result.toList() if (filesUsed.isEmpty()) {
// This is the case when we depend on the whole module rather than on a number of files.
moduleDependencies.add(library)
addAllDependencies(cache)
} else {
fileDependencies.getOrPut(library) { mutableSetOf() }.addAll(filesUsed)
addDependencies(cache, filesUsed)
}
}
}
allDependencies = moduleDependencies.map { CachedBitcodeDependency.WholeModule(it) } +
fileDependencies.filterNot { it.key in moduleDependencies }
.map { (library, files) -> CachedBitcodeDependency.CertainFiles(library, files.toList()) }
}
private fun addAllDependencies(cachedLibrary: CachedLibraries.Cache) {
cachedLibrary.bitcodeDependencies.forEach { addDependency(it) }
}
private fun addDependencies(cachedLibrary: CachedLibraries.Cache, files: List<String>) = when (cachedLibrary) {
is CachedLibraries.Cache.Monolithic -> addAllDependencies(cachedLibrary)
is CachedLibraries.Cache.PerFile ->
files.forEach { file ->
cachedLibrary.getFileDependencies(file).forEach { addDependency(it) }
}
}
private fun addDependency(dependency: CachedLibraries.BitcodeDependency) {
val dependencyLib = (allLibraries[dependency.libName]
?: error("Bitcode dependency to an unknown library: ${dependency.libName}")) as KonanLibrary
if (dependencyLib in moduleDependencies) return
val cachedDependency = context.config.cachedLibraries.getLibraryCache(dependencyLib)
?: error("Library ${dependency.libName} is expected to be cached")
when (dependency) {
is CachedLibraries.BitcodeDependency.WholeModule -> {
moduleDependencies.add(dependencyLib)
addAllDependencies(cachedDependency)
}
is CachedLibraries.BitcodeDependency.CertainFiles -> {
val handledFiles = fileDependencies.getOrPut(dependencyLib) { mutableSetOf() }
val notHandledFiles = dependency.files.toMutableSet()
notHandledFiles.removeAll(handledFiles)
handledFiles.addAll(notHandledFiles)
if (notHandledFiles.isNotEmpty())
addDependencies(cachedDependency, notHandledFiles.toList())
}
}
}
}
val allCachedBitcodeDependencies: List<CachedBitcodeDependency> by lazy {
CachedBitcodeDependenciesComputer().allDependencies
} }
val allNativeDependencies: List<KonanLibrary> by lazy { val allNativeDependencies: List<KonanLibrary> by lazy {
(nativeDependenciesToLink + allCachedBitcodeDependencies).distinct() (nativeDependenciesToLink + allCachedBitcodeDependencies.map { it.library } /* Native dependencies are per library */).distinct()
} }
val allBitcodeDependencies: List<KonanLibrary> by lazy { val allBitcodeDependencies: List<CachedBitcodeDependency> by lazy {
val allNonCachedDependencies = context.librariesWithDependencies.filter { val allBitcodeDependencies = mutableMapOf<KonanLibrary, CachedBitcodeDependency>()
context.config.cachedLibraries.getLibraryCache(it) == null || it == context.config.libraryToCache?.klib for (library in context.librariesWithDependencies) {
if (context.config.cachedLibraries.getLibraryCache(library) == null || library == context.config.libraryToCache?.klib)
allBitcodeDependencies[library] = CachedBitcodeDependency.WholeModule(library)
} }
val set = (allNonCachedDependencies + allCachedBitcodeDependencies).toSet() for (dependency in allCachedBitcodeDependencies)
allBitcodeDependencies[dependency.library] = dependency
// This list is used in particular to build the libraries' initializers chain. // This list is used in particular to build the libraries' initializers chain.
// The initializers must be called in the topological order, so make sure that the // The initializers must be called in the topological order, so make sure that the
// libraries list being returned is also toposorted. // libraries list being returned is also toposorted.
context.config.resolvedLibraries context.config.resolvedLibraries
.getFullList(TopologicalLibraryOrder) .getFullList(TopologicalLibraryOrder)
.map { it as KonanLibrary } .mapNotNull { allBitcodeDependencies[it as KonanLibrary] }
.filter { it in set }
} }
val bitcodeToLink: List<KonanLibrary> by lazy { val bitcodeToLink: List<KonanLibrary> by lazy {
@@ -663,35 +774,31 @@ internal class Llvm(private val generationState: NativeGenerationState, val modu
else -> "__gxx_personality_v0" else -> "__gxx_personality_v0"
} }
val cxxStdTerminate = externalFunction(LlvmFunctionProto( val cxxStdTerminate = externalNativeRuntimeFunction(
"_ZSt9terminatev", // mangled C++ 'std::terminate' "_ZSt9terminatev", // mangled C++ 'std::terminate'
returnType = LlvmRetType(voidType), returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind)
origin = context.standardLlvmSymbolsOrigin )
))
val gxxPersonalityFunction = externalFunction(LlvmFunctionProto( val gxxPersonalityFunction = externalNativeRuntimeFunction(
personalityFunctionName, personalityFunctionName,
returnType = LlvmRetType(int32Type), returnType = LlvmRetType(int32Type),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
isVararg = true, isVararg = true
origin = context.standardLlvmSymbolsOrigin )
))
val cxaBeginCatchFunction = externalFunction(LlvmFunctionProto( val cxaBeginCatchFunction = externalNativeRuntimeFunction(
"__cxa_begin_catch", "__cxa_begin_catch",
returnType = LlvmRetType(int8PtrType), returnType = LlvmRetType(int8PtrType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
parameterTypes = listOf(LlvmParamType(int8PtrType)), parameterTypes = listOf(LlvmParamType(int8PtrType))
origin = context.standardLlvmSymbolsOrigin )
))
val cxaEndCatchFunction = externalFunction(LlvmFunctionProto( val cxaEndCatchFunction = externalNativeRuntimeFunction(
"__cxa_end_catch", "__cxa_end_catch",
returnType = LlvmRetType(voidType), returnType = LlvmRetType(voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind), functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind)
origin = context.standardLlvmSymbolsOrigin )
))
private fun getSizeOfReturnTypeInBits(functionPointer: LLVMValueRef): Long { private fun getSizeOfReturnTypeInBits(functionPointer: LLVMValueRef): Long {
// LLVMGetElementType is called because we need to dereference a pointer to function. // LLVMGetElementType is called because we need to dereference a pointer to function.
@@ -12,12 +12,17 @@ import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.SyntheticModulesOrigin import org.jetbrains.kotlin.library.metadata.SyntheticModulesOrigin
import org.jetbrains.kotlin.library.metadata.klibModuleOrigin import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
internal interface LlvmImports { internal interface LlvmImports {
fun add(origin: CompiledKlibModuleOrigin, onlyBitcode: Boolean = false) data class LibraryFile(val library: KotlinLibrary, val fqName: String, val filePath: String)
fun add(origin: CompiledKlibFileOrigin, onlyBitcode: Boolean = false)
fun bitcodeIsUsed(library: KonanLibrary): Boolean fun bitcodeIsUsed(library: KonanLibrary): Boolean
fun nativeDependenciesAreUsed(library: KonanLibrary): Boolean fun nativeDependenciesAreUsed(library: KonanLibrary): Boolean
fun usedBitcode(): List<LibraryFile>
} }
internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKlibModuleOrigin internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKlibModuleOrigin
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.*
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.getClass
import org.jetbrains.kotlin.ir.util.dump
import org.jetbrains.kotlin.ir.util.findAnnotation import org.jetbrains.kotlin.ir.util.findAnnotation
internal enum class IntrinsicType { internal enum class IntrinsicType {
@@ -440,21 +439,18 @@ internal class IntrinsicGenerator(private val environment: IntrinsicGeneratorEnv
val functionReturnType = LlvmRetType(llvm.int8PtrType) val functionReturnType = LlvmRetType(llvm.int8PtrType)
val functionParameterTypes = listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)) val functionParameterTypes = listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType))
val libobjc = context.standardLlvmSymbolsOrigin val normalMessenger = codegen.llvm.externalNativeRuntimeFunction(
val normalMessenger = codegen.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSend$messengerNameSuffix", "objc_msgSend$messengerNameSuffix",
functionReturnType, functionReturnType,
functionParameterTypes, functionParameterTypes,
isVararg = true, isVararg = true
origin = libobjc )
)) val superMessenger = codegen.llvm.externalNativeRuntimeFunction(
val superMessenger = codegen.llvm.externalFunction(LlvmFunctionProto(
"objc_msgSendSuper$messengerNameSuffix", "objc_msgSendSuper$messengerNameSuffix",
functionReturnType, functionReturnType,
functionParameterTypes, functionParameterTypes,
isVararg = true, isVararg = true
origin = libobjc )
))
val superClass = args.single() val superClass = args.single()
val messenger = LLVMBuildSelect(builder, val messenger = LLVMBuildSelect(builder,
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_STANDA
import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER import org.jetbrains.kotlin.backend.konan.lower.DECLARATION_ORIGIN_STATIC_THREAD_LOCAL_INITIALIZER
import org.jetbrains.kotlin.builtins.UnsignedType import org.jetbrains.kotlin.builtins.UnsignedType
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.IrStatement
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
@@ -1627,21 +1628,18 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
} else { } else {
// e.g. ObjCObject, ObjCObjectBase etc. // e.g. ObjCObject, ObjCObjectBase etc.
if (dstClass.isObjCMetaClass()) { if (dstClass.isObjCMetaClass()) {
val isClassProto = LlvmFunctionProto( val isClass = llvm.externalNativeRuntimeFunction(
"object_isClass", "object_isClass",
LlvmRetType(llvm.int8Type), LlvmRetType(llvm.int8Type),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = context.standardLlvmSymbolsOrigin
) )
val isClass = llvm.externalFunction(isClassProto)
call(isClass, listOf(objCObject)).let { call(isClass, listOf(objCObject)).let {
functionGenerationContext.icmpNe(it, llvm.int8(0)) functionGenerationContext.icmpNe(it, llvm.int8(0))
} }
} else if (dstClass.isObjCProtocolClass()) { } else if (dstClass.isObjCProtocolClass()) {
// Note: it is not clear whether this class should be looked up this way. // Note: it is not clear whether this class should be looked up this way.
// clang does the same, however swiftc uses dynamic lookup. // clang does the same, however swiftc uses dynamic lookup.
val protocolClass = val protocolClass = functionGenerationContext.getObjCClass("Protocol", CompiledKlibFileOrigin.StdlibRuntime)
functionGenerationContext.getObjCClass("Protocol", context.standardLlvmSymbolsOrigin)
call( call(
llvm.Kotlin_Interop_IsObjectKindOfClass, llvm.Kotlin_Interop_IsObjectKindOfClass,
listOf(objCObject, protocolClass) listOf(objCObject, protocolClass)
@@ -2496,7 +2494,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
val protocolGetterProto = LlvmFunctionProto( val protocolGetterProto = LlvmFunctionProto(
protocolGetterName, protocolGetterName,
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
origin = irClass.llvmSymbolOrigin, origin = generationState.computeOrigin(irClass),
independent = true // Protocol is header-only declaration. independent = true // Protocol is header-only declaration.
) )
val protocolGetter = llvm.externalFunction(protocolGetterProto) val protocolGetter = llvm.externalFunction(protocolGetterProto)
@@ -2679,7 +2677,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) {
// When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well. // When some dynamic caches are used, we consider that stdlib is in the dynamic cache as well.
// Runtime is linked into stdlib module only, so import runtime global from it. // Runtime is linked into stdlib module only, so import runtime global from it.
val global = codegen.importGlobal(name, value.llvmType, context.standardLlvmSymbolsOrigin) val global = codegen.importNativeRuntimeGlobal(name, value.llvmType)
val initializer = generateFunctionNoRuntime(codegen, functionType(llvm.voidType, false), "") { val initializer = generateFunctionNoRuntime(codegen, functionType(llvm.voidType, false), "") {
store(value.llvm, global) store(value.llvm, global)
ret(null) ret(null)
@@ -2689,7 +2687,7 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
llvm.otherStaticInitializers += initializer llvm.otherStaticInitializers += initializer
} else { } else {
generationState.llvmImports.add(context.standardLlvmSymbolsOrigin) generationState.llvmImports.add(CompiledKlibFileOrigin.StdlibRuntime)
// Define a strong runtime global. It'll overrule a weak global defined in a statically linked runtime. // Define a strong runtime global. It'll overrule a weak global defined in a statically linked runtime.
val global = llvm.staticData.placeGlobal(name, value, true) val global = llvm.staticData.placeGlobal(name, value, true)
@@ -2759,11 +2757,9 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
//-------------------------------------------------------------------------// //-------------------------------------------------------------------------//
fun appendStaticInitializers() { fun appendStaticInitializers() {
// Note: the list of libraries is topologically sorted (in order for initializers to be called correctly). // Note: the list of libraries is topologically sorted (in order for initializers to be called correctly).
val libraries = (llvm.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */) val dependencies = (llvm.allBitcodeDependencies + listOf(null)/* Null for "current" non-library module */)
val libraryToInitializers = libraries.associateWith { val libraryToInitializers = dependencies.associate { it?.library to mutableListOf<LLVMValueRef>() }
mutableListOf<LLVMValueRef>()
}
llvm.irStaticInitializers.forEach { llvm.irStaticInitializers.forEach {
val library = it.konanLibrary val library = it.konanLibrary
@@ -2783,7 +2779,8 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
kVoidFuncType kVoidFuncType
).also { LLVMSetLinkage(it, LLVMLinkage.LLVMExternalLinkage) } ).also { LLVMSetLinkage(it, LLVMLinkage.LLVMExternalLinkage) }
val ctorFunctions = libraries.flatMap { library -> val ctorFunctions = dependencies.flatMap { dependency ->
val library = dependency?.library
val initializers = libraryToInitializers.getValue(library) val initializers = libraryToInitializers.getValue(library)
val ctorName = when { val ctorName = when {
@@ -2811,12 +2808,16 @@ internal class CodeGeneratorVisitor(val generationState: NativeGenerationState,
val cache = context.config.cachedLibraries.getLibraryCache(library) val cache = context.config.cachedLibraries.getLibraryCache(library)
?: error("Library $library is expected to be cached") ?: error("Library $library is expected to be cached")
when (cache.granularity) { when (cache) {
CachedLibraries.Granularity.MODULE -> listOf(addCtorFunction(ctorName)) is CachedLibraries.Cache.Monolithic -> listOf(addCtorFunction(ctorName))
CachedLibraries.Granularity.FILE -> { is CachedLibraries.Cache.PerFile -> {
context.irLinker.klibToModuleDeserializerMap[library]!!.sortedFileIds.map { val files = when (dependency) {
addCtorFunction(fileCtorName(library.uniqueName, it)) is Llvm.CachedBitcodeDependency.WholeModule ->
context.irLinker.klibToModuleDeserializerMap[library]!!.sortedFileIds
is Llvm.CachedBitcodeDependency.CertainFiles ->
dependency.files
} }
files.map { addCtorFunction(fileCtorName(library.uniqueName, it)) }
} }
} }
} }
@@ -28,11 +28,11 @@ internal class KotlinObjCClassInfoGenerator(override val generationState: Native
val classMethods = companionObject?.generateMethodDescs().orEmpty() val classMethods = companionObject?.generateMethodDescs().orEmpty()
val superclassName = irClass.getSuperClassNotAny()!!.let { val superclassName = irClass.getSuperClassNotAny()!!.let {
llvm.imports.add(it.llvmSymbolOrigin) llvm.imports.add(generationState.computeOrigin(it))
it.descriptor.getExternalObjCClassBinaryName() it.descriptor.getExternalObjCClassBinaryName()
} }
val protocolNames = irClass.getSuperInterfaces().map { val protocolNames = irClass.getSuperInterfaces().map {
llvm.imports.add(it.llvmSymbolOrigin) llvm.imports.add(generationState.computeOrigin(it))
it.name.asString().removeSuffix("Protocol") it.name.asString().removeSuffix("Protocol")
} }
@@ -161,7 +161,7 @@ internal fun CodeGenerator.kotlinObjCClassInfo(irClass: IrClass): LLVMValueRef {
importGlobal( importGlobal(
irClass.kotlinObjCClassInfoSymbolName, irClass.kotlinObjCClassInfoSymbolName,
runtime.kotlinObjCClassInfo, runtime.kotlinObjCClassInfo,
origin = irClass.llvmSymbolOrigin generationState.computeOrigin(irClass)
) )
} else { } else {
llvmDeclarations.forClass(irClass).objCDeclarations!!.classInfoGlobal.llvmGlobal llvmDeclarations.forClass(irClass).objCDeclarations!!.classInfoGlobal.llvmGlobal
@@ -104,9 +104,8 @@ internal class KotlinStaticData(override val generationState: NativeGenerationSt
UniqueKind.EMPTY_ARRAY -> context.ir.symbols.array.owner UniqueKind.EMPTY_ARRAY -> context.ir.symbols.array.owner
} }
return if (isExternal(descriptor)) { return if (isExternal(descriptor)) {
constPointer(importGlobal( constPointer(
kind.llvmName, runtime.objHeaderType, origin = descriptor.llvmSymbolOrigin importGlobal(kind.llvmName, runtime.objHeaderType, generationState.computeOrigin(descriptor)))
))
} else { } else {
generationState.llvmDeclarations.forUnique(kind).pointer generationState.llvmDeclarations.forUnique(kind).pointer
} }
@@ -12,13 +12,14 @@ import llvm.*
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.RuntimeNames import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrConstructor
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.types.isNothing import org.jetbrains.kotlin.ir.types.isNothing
import org.jetbrains.kotlin.ir.util.hasAnnotation import org.jetbrains.kotlin.ir.util.hasAnnotation
import org.jetbrains.kotlin.ir.util.isSuspend import org.jetbrains.kotlin.ir.util.isSuspend
import org.jetbrains.kotlin.ir.util.isThrowable import org.jetbrains.kotlin.ir.util.isThrowable
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
/** /**
* Add attributes to LLVM function declaration and its invocation. * Add attributes to LLVM function declaration and its invocation.
@@ -156,14 +157,14 @@ internal class LlvmFunctionProto(
returnType: LlvmRetType, returnType: LlvmRetType,
parameterTypes: List<LlvmParamType> = emptyList(), parameterTypes: List<LlvmParamType> = emptyList(),
functionAttributes: List<LlvmFunctionAttribute> = emptyList(), functionAttributes: List<LlvmFunctionAttribute> = emptyList(),
val origin: CompiledKlibModuleOrigin, val origin: CompiledKlibFileOrigin,
isVararg: Boolean = false, isVararg: Boolean = false,
val independent: Boolean = false, val independent: Boolean = false,
) : LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes) { ) : LlvmFunctionSignature(returnType, parameterTypes, isVararg, functionAttributes) {
constructor( constructor(
name: String, name: String,
signature: LlvmFunctionSignature, signature: LlvmFunctionSignature,
origin: CompiledKlibModuleOrigin, origin: CompiledKlibFileOrigin,
independent: Boolean = false, independent: Boolean = false,
) : this(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, origin, signature.isVararg, independent) ) : this(name, signature.returnType, signature.parameterTypes, signature.functionAttributes, origin, signature.isVararg, independent)
@@ -172,7 +173,7 @@ internal class LlvmFunctionProto(
returnType = contextUtils.getLlvmFunctionReturnType(irFunction), returnType = contextUtils.getLlvmFunctionReturnType(irFunction),
parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction), parameterTypes = contextUtils.getLlvmFunctionParameterTypes(irFunction),
functionAttributes = inferFunctionAttributes(contextUtils, irFunction), functionAttributes = inferFunctionAttributes(contextUtils, irFunction),
origin = irFunction.llvmSymbolOrigin, origin = contextUtils.generationState.computeOrigin(irFunction),
independent = irFunction.hasAnnotation(RuntimeNames.independent) independent = irFunction.hasAnnotation(RuntimeNames.independent)
) )
} }
@@ -7,7 +7,7 @@ package org.jetbrains.kotlin.backend.konan.llvm
import kotlinx.cinterop.* import kotlinx.cinterop.*
import llvm.* import llvm.*
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
internal val LLVMValueRef.type: LLVMTypeRef internal val LLVMValueRef.type: LLVMTypeRef
get() = LLVMTypeOf(this)!! get() = LLVMTypeOf(this)!!
@@ -170,19 +170,25 @@ internal fun ContextUtils.addGlobal(name: String, type: LLVMTypeRef, isExported:
return LLVMAddGlobal(llvm.module, type, name)!! return LLVMAddGlobal(llvm.module, type, name)!!
} }
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibModuleOrigin): LLVMValueRef { private fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef): LLVMValueRef {
llvm.imports.add(origin)
val found = LLVMGetNamedGlobal(llvm.module, name) val found = LLVMGetNamedGlobal(llvm.module, name)
return if (found != null) { return if (found == null)
assert (getGlobalType(found) == type)
assert (LLVMGetInitializer(found) == null) { "$name is already declared in the current module" }
found
} else {
addGlobal(name, type, isExported = false) addGlobal(name, type, isExported = false)
else {
require(getGlobalType(found) == type)
require(LLVMGetInitializer(found) == null) { "$name is already declared in the current module" }
found
} }
} }
internal fun ContextUtils.importGlobal(name: String, type: LLVMTypeRef, origin: CompiledKlibFileOrigin) =
importGlobal(name, type).also { llvm.imports.add(origin) }
internal fun ContextUtils.importObjCGlobal(name: String, type: LLVMTypeRef) = importGlobal(name, type)
internal fun ContextUtils.importNativeRuntimeGlobal(name: String, type: LLVMTypeRef) =
importGlobal(name, type, CompiledKlibFileOrigin.StdlibRuntime)
internal abstract class AddressAccess { internal abstract class AddressAccess {
abstract fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef abstract fun getAddress(generationContext: FunctionGenerationContext?): LLVMValueRef
} }
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.builtins.PrimitiveType
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.name.FqName
internal class RTTIGenerator(override val generationState: NativeGenerationState) : ContextUtils { internal class RTTIGenerator(override val generationState: NativeGenerationState) : ContextUtils {
@@ -147,10 +146,8 @@ internal class RTTIGenerator(override val generationState: NativeGenerationState
staticData.kotlinStringLiteral(string) staticData.kotlinStringLiteral(string)
} }
private val EXPORT_TYPE_INFO_FQ_NAME = FqName.fromSegments(listOf("kotlin", "native", "internal", "ExportTypeInfo"))
private fun exportTypeInfoIfRequired(irClass: IrClass, typeInfoGlobal: LLVMValueRef?) { private fun exportTypeInfoIfRequired(irClass: IrClass, typeInfoGlobal: LLVMValueRef?) {
val annotation = irClass.annotations.findAnnotation(EXPORT_TYPE_INFO_FQ_NAME) val annotation = irClass.annotations.findAnnotation(RuntimeNames.exportTypeInfoAnnotation)
if (annotation != null) { if (annotation != null) {
val name = annotation.getAnnotationStringValue()!! val name = annotation.getAnnotationStringValue()!!
// TODO: use LLVMAddAlias. // TODO: use LLVMAddAlias.
@@ -28,48 +28,40 @@ internal open class ObjCCodeGenerator(val codegen: CodeGenerator) {
} }
private val objcMsgSend = constPointer( private val objcMsgSend = constPointer(
llvm.externalFunction(LlvmFunctionProto( llvm.externalNativeRuntimeFunction(
"objc_msgSend", "objc_msgSend",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType), LlvmParamType(llvm.int8PtrType)),
isVararg = true, isVararg = true
origin = context.stdlibModule.llvmSymbolOrigin ).llvmValue
)).llvmValue
) )
val objcRelease = run { val objcRelease = llvm.externalNativeRuntimeFunction(
val proto = LlvmFunctionProto(
"llvm.objc.release", "llvm.objc.release",
LlvmRetType(llvm.voidType), LlvmRetType(llvm.voidType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType)),
listOf(LlvmFunctionAttribute.NoUnwind), listOf(LlvmFunctionAttribute.NoUnwind)
origin = context.stdlibModule.llvmSymbolOrigin
) )
llvm.externalFunction(proto)
}
val objcAlloc = llvm.externalFunction(LlvmFunctionProto( val objcAlloc = llvm.externalNativeRuntimeFunction(
"objc_alloc", "objc_alloc",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = context.stdlibModule.llvmSymbolOrigin )
))
val objcAutoreleaseReturnValue = llvm.externalFunction(LlvmFunctionProto( val objcAutoreleaseReturnValue = llvm.externalNativeRuntimeFunction(
"llvm.objc.autoreleaseReturnValue", "llvm.objc.autoreleaseReturnValue",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType)),
listOf(LlvmFunctionAttribute.NoUnwind), listOf(LlvmFunctionAttribute.NoUnwind)
origin = context.stdlibModule.llvmSymbolOrigin )
))
val objcRetainAutoreleasedReturnValue = llvm.externalFunction(LlvmFunctionProto( val objcRetainAutoreleasedReturnValue = llvm.externalNativeRuntimeFunction(
"llvm.objc.retainAutoreleasedReturnValue", "llvm.objc.retainAutoreleasedReturnValue",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType)),
listOf(LlvmFunctionAttribute.NoUnwind), listOf(LlvmFunctionAttribute.NoUnwind)
origin = context.stdlibModule.llvmSymbolOrigin )
))
val objcRetainAutoreleasedReturnValueMarker: LLVMValueRef? by lazy { val objcRetainAutoreleasedReturnValueMarker: LLVMValueRef? by lazy {
// See emitAutoreleasedReturnValueMarker in Clang. // See emitAutoreleasedReturnValueMarker in Clang.
@@ -7,7 +7,6 @@ package org.jetbrains.kotlin.backend.konan.llvm.objc
import llvm.* import llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin
/** /**
* This class provides methods to generate Objective-C RTTI and other data. * This class provides methods to generate Objective-C RTTI and other data.
@@ -75,18 +74,14 @@ internal class ObjCDataGenerator(val codegen: CodeGenerator) {
val globalName = prefix + name val globalName = prefix + name
// TODO: refactor usages and use [Global] class. // TODO: refactor usages and use [Global] class.
val llvmGlobal = LLVMGetNamedGlobal(llvm.module, globalName) ?: val llvmGlobal = LLVMGetNamedGlobal(llvm.module, globalName)
codegen.importGlobal(globalName, classObjectType, CurrentKlibModuleOrigin) ?: codegen.importObjCGlobal(globalName, classObjectType)
return constPointer(llvmGlobal) return constPointer(llvmGlobal)
} }
private val emptyCache = constPointer( private val emptyCache = constPointer(
codegen.importGlobal( codegen.importObjCGlobal("_objc_empty_cache", codegen.runtime.objCCache)
"_objc_empty_cache",
codegen.runtime.objCCache,
CurrentKlibModuleOrigin
)
) )
fun emitEmptyClass(name: String, superName: String) { fun emitEmptyClass(name: String, superName: String) {
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.objcexport.BlockPointerBridge import org.jetbrains.kotlin.backend.konan.objcexport.BlockPointerBridge
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
import org.jetbrains.kotlin.ir.util.simpleFunctions import org.jetbrains.kotlin.ir.util.simpleFunctions
import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.util.OperatorNameConventions import org.jetbrains.kotlin.util.OperatorNameConventions
internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter( internal fun ObjCExportCodeGeneratorBase.generateBlockToKotlinFunctionConverter(
@@ -315,11 +314,7 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
ret(llvm.kNullInt8Ptr) ret(llvm.kNullInt8Ptr)
} }
val isa = codegen.importGlobal( val isa = codegen.importObjCGlobal("_NSConcreteStackBlock", llvm.int8PtrType)
"_NSConcreteStackBlock",
llvm.int8PtrType,
CurrentKlibModuleOrigin
)
val flags = llvm.int32((1 shl 25) or (1 shl 30) or (1 shl 31)) val flags = llvm.int32((1 shl 25) or (1 shl 30) or (1 shl 31))
val reserved = llvm.int32(0) val reserved = llvm.int32(0)
@@ -350,12 +345,8 @@ internal class BlockGenerator(private val codegen: CodeGenerator) {
} }
private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable private val ObjCExportCodeGeneratorBase.retainBlock: LlvmCallable
get() { get() = llvm.externalNativeRuntimeFunction(
val functionProto = LlvmFunctionProto(
"objc_retainBlock", "objc_retainBlock",
LlvmRetType(llvm.int8PtrType), LlvmRetType(llvm.int8PtrType),
listOf(LlvmParamType(llvm.int8PtrType)), listOf(LlvmParamType(llvm.int8PtrType))
origin = CurrentKlibModuleOrigin
) )
return llvm.externalFunction(functionProto)
}
@@ -16,7 +16,6 @@ import org.jetbrains.kotlin.backend.konan.descriptors.allOverriddenFunctions
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols import org.jetbrains.kotlin.backend.konan.ir.KonanSymbols
import org.jetbrains.kotlin.backend.konan.ir.isUnit import org.jetbrains.kotlin.backend.konan.ir.isUnit
import org.jetbrains.kotlin.backend.konan.ir.llvmSymbolOrigin
import org.jetbrains.kotlin.backend.konan.ir.superClasses import org.jetbrains.kotlin.backend.konan.ir.superClasses
import org.jetbrains.kotlin.backend.konan.llvm.* import org.jetbrains.kotlin.backend.konan.llvm.*
import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator import org.jetbrains.kotlin.backend.konan.llvm.objc.ObjCCodeGenerator
@@ -26,6 +25,7 @@ import org.jetbrains.kotlin.backend.konan.objcexport.*
import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract import org.jetbrains.kotlin.backend.konan.serialization.resolveFakeOverrideMaybeAbstract
import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.descriptors.Modality
import org.jetbrains.kotlin.library.metadata.CompiledKlibFileOrigin
import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.expressions.IrClassReference import org.jetbrains.kotlin.ir.expressions.IrClassReference
@@ -40,8 +40,6 @@ import org.jetbrains.kotlin.ir.types.isUnit
import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.konan.target.AppleConfigurables import org.jetbrains.kotlin.konan.target.AppleConfigurables
import org.jetbrains.kotlin.konan.target.LinkerOutputKind import org.jetbrains.kotlin.konan.target.LinkerOutputKind
import org.jetbrains.kotlin.library.metadata.CompiledKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.CurrentKlibModuleOrigin
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors import org.jetbrains.kotlin.psi2ir.descriptors.IrBuiltInsOverDescriptors
import org.jetbrains.kotlin.utils.DFS import org.jetbrains.kotlin.utils.DFS
@@ -223,15 +221,6 @@ internal open class ObjCExportCodeGeneratorBase(codegen: CodeGenerator) : ObjCCo
val rttiGenerator = RTTIGenerator(generationState) val rttiGenerator = RTTIGenerator(generationState)
private val objcTerminate: LlvmCallable by lazy {
llvm.externalFunction(LlvmFunctionProto(
"objc_terminate",
LlvmRetType(llvm.voidType),
functionAttributes = listOf(LlvmFunctionAttribute.NoUnwind),
origin = CurrentKlibModuleOrigin
))
}
fun dispose() { fun dispose() {
rttiGenerator.dispose() rttiGenerator.dispose()
} }
@@ -478,7 +467,7 @@ internal class ObjCExportCodeGenerator(
replaceExternalWeakOrCommonGlobal( replaceExternalWeakOrCommonGlobal(
"Kotlin_ObjCInterop_uniquePrefix", "Kotlin_ObjCInterop_uniquePrefix",
codegen.staticData.cStringLiteral(namer.topLevelNamePrefix), codegen.staticData.cStringLiteral(namer.topLevelNamePrefix),
context.standardLlvmSymbolsOrigin CompiledKlibFileOrigin.StdlibRuntime
) )
emitSelectorsHolder() emitSelectorsHolder()
@@ -524,7 +513,7 @@ internal class ObjCExportCodeGenerator(
val sortedAdaptersPointer = staticData.placeGlobalConstArray("", type, sortedAdapters) val sortedAdaptersPointer = staticData.placeGlobalConstArray("", type, sortedAdapters)
// Note: this globals replace runtime globals with weak linkage: // Note: this globals replace runtime globals with weak linkage:
val origin = context.standardLlvmSymbolsOrigin val origin = CompiledKlibFileOrigin.StdlibRuntime
replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer, origin) replaceExternalWeakOrCommonGlobal(prefix, sortedAdaptersPointer, origin)
replaceExternalWeakOrCommonGlobal("${prefix}Num", llvm.constInt32(sortedAdapters.size), origin) replaceExternalWeakOrCommonGlobal("${prefix}Num", llvm.constInt32(sortedAdapters.size), origin)
} }
@@ -537,7 +526,7 @@ internal class ObjCExportCodeGenerator(
replaceExternalWeakOrCommonGlobal( replaceExternalWeakOrCommonGlobal(
"Kotlin_ObjCExport_initTypeAdapters", "Kotlin_ObjCExport_initTypeAdapters",
llvm.constInt1(true), llvm.constInt1(true),
context.standardLlvmSymbolsOrigin CompiledKlibFileOrigin.StdlibRuntime
) )
} }
} }
@@ -704,7 +693,7 @@ internal class ObjCExportCodeGenerator(
private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal( private fun ObjCExportCodeGenerator.replaceExternalWeakOrCommonGlobal(
name: String, name: String,
value: ConstValue, value: ConstValue,
origin: CompiledKlibModuleOrigin origin: CompiledKlibFileOrigin
) { ) {
// TODO: A similar mechanism is used in `IrToBitcode.overrideRuntimeGlobal`. Consider merging them. // TODO: A similar mechanism is used in `IrToBitcode.overrideRuntimeGlobal`. Consider merging them.
if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) { if (generationState.llvmModuleSpecification.importsKotlinDeclarationsFromOtherSharedLibraries()) {
@@ -742,7 +731,7 @@ private fun ObjCExportCodeGenerator.setObjCExportTypeInfo(
replaceExternalWeakOrCommonGlobal( replaceExternalWeakOrCommonGlobal(
irClass.writableTypeInfoSymbolName, irClass.writableTypeInfoSymbolName,
writableTypeInfoValue, writableTypeInfoValue,
irClass.llvmSymbolOrigin generationState.computeOrigin(irClass)
) )
} else { } else {
setOwnWritableTypeInfo(irClass, writableTypeInfoValue) setOwnWritableTypeInfo(irClass, writableTypeInfoValue)
@@ -924,11 +913,8 @@ private fun ObjCExportCodeGenerator.emitSpecialClassesConvertions() {
private fun ObjCExportCodeGenerator.emitCollectionConverters() { private fun ObjCExportCodeGenerator.emitCollectionConverters() {
fun importConverter(name: String): ConstPointer = constPointer(llvm.externalFunction(LlvmFunctionProto( fun importConverter(name: String): ConstPointer =
name, constPointer(llvm.externalNativeRuntimeFunction(name, kotlinToObjCFunctionType).llvmValue)
kotlinToObjCFunctionType,
origin = CurrentKlibModuleOrigin
)).llvmValue)
setObjCExportTypeInfo( setObjCExportTypeInfo(
symbols.list.owner, symbols.list.owner,
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.backend.konan.lower
import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_FUNCTION_CLASS import org.jetbrains.kotlin.backend.konan.DECLARATION_ORIGIN_FUNCTION_CLASS
import org.jetbrains.kotlin.backend.konan.NativeGenerationState import org.jetbrains.kotlin.backend.konan.NativeGenerationState
import org.jetbrains.kotlin.backend.konan.KonanFqNames
import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker import org.jetbrains.kotlin.backend.konan.serialization.KonanIrLinker
import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerIr import org.jetbrains.kotlin.backend.konan.serialization.KonanManglerIr
import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.IrElement
@@ -21,7 +22,10 @@ internal class CacheInfoBuilder(
private val moduleDeserializer: KonanIrLinker.KonanPartialModuleDeserializer, private val moduleDeserializer: KonanIrLinker.KonanPartialModuleDeserializer,
private val irModule: IrModuleFragment private val irModule: IrModuleFragment
) { ) {
fun build() = irModule.acceptChildrenVoid(object : IrElementVisitorVoid { fun build() = irModule.files.forEach { irFile ->
var hasEagerlyInitializedProperties = false
irFile.acceptChildrenVoid(object : IrElementVisitorVoid {
override fun visitElement(element: IrElement) { override fun visitElement(element: IrElement) {
element.acceptChildrenVoid(this) element.acceptChildrenVoid(this)
} }
@@ -46,8 +50,22 @@ internal class CacheInfoBuilder(
trackCallees(declaration) trackCallees(declaration)
} }
} }
override fun visitProperty(declaration: IrProperty) {
declaration.acceptChildrenVoid(this)
if (declaration.parent == irFile
&& declaration.hasAnnotation(KonanFqNames.eagerInitialization)
) {
hasEagerlyInitializedProperties = true
}
}
}) })
if (hasEagerlyInitializedProperties)
generationState.eagerInitializedFiles.add(moduleDeserializer.buildEagerInitializedFile(irFile))
}
private val IrDeclaration.isExported private val IrDeclaration.isExported
get() = with(KonanManglerIr) { isExported(compatibleMode = moduleDeserializer.compatibilityMode.oldSignatures) } get() = with(KonanManglerIr) { isExported(compatibleMode = moduleDeserializer.compatibilityMode.oldSignatures) }
@@ -51,6 +51,7 @@ internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: I
returnType = irClass.parentAsClass.defaultType returnType = irClass.parentAsClass.defaultType
}.apply { }.apply {
parent = irClass.getPackageFragment() parent = irClass.getPackageFragment()
attributeOwnerId = irClass // To be able to get the file.
addValueParameter { addValueParameter {
name = Name.identifier("innerClass") name = Name.identifier("innerClass")
@@ -73,6 +74,7 @@ internal class CachesAbiSupport(mapping: NativeMapping, private val irFactory: I
returnType = actualField.type returnType = actualField.type
}.apply { }.apply {
parent = irProperty.getPackageFragment() parent = irProperty.getPackageFragment()
attributeOwnerId = irProperty // To be able to get the file.
(owner as? IrClass)?.let { (owner as? IrClass)?.let {
addValueParameter { addValueParameter {
@@ -150,6 +152,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS
private val cachesAbiSupport = generationState.context.cachesAbiSupport private val cachesAbiSupport = generationState.context.cachesAbiSupport
private val innerClassesSupport = generationState.context.innerClassesSupport private val innerClassesSupport = generationState.context.innerClassesSupport
private val llvmImports = generationState.llvmImports private val llvmImports = generationState.llvmImports
private val irLinker = generationState.context.irLinker
override fun lower(irFile: IrFile) { override fun lower(irFile: IrFile) {
irFile.transformChildrenVoid(this) irFile.transformChildrenVoid(this)
@@ -168,7 +171,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS
irClass?.isInner == true && innerClassesSupport.getOuterThisField(irClass) == field -> { irClass?.isInner == true && innerClassesSupport.getOuterThisField(irClass) == field -> {
val accessor = cachesAbiSupport.getOuterThisAccessor(irClass) val accessor = cachesAbiSupport.getOuterThisAccessor(irClass)
llvmImports.add(irClass.llvmSymbolOrigin) llvmImports.add(generationState.computeOrigin(irClass))
return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply { return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply {
putValueArgument(0, expression.receiver) putValueArgument(0, expression.receiver)
} }
@@ -176,7 +179,7 @@ internal class ImportCachesAbiTransformer(val generationState: NativeGenerationS
property?.isLateinit == true -> { property?.isLateinit == true -> {
val accessor = cachesAbiSupport.getLateinitPropertyAccessor(property) val accessor = cachesAbiSupport.getLateinitPropertyAccessor(property)
llvmImports.add(property.llvmSymbolOrigin) llvmImports.add(generationState.computeOrigin(property))
return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply { return irCall(expression.startOffset, expression.endOffset, accessor, emptyList()).apply {
if (irClass != null) if (irClass != null)
putValueArgument(0, expression.receiver) putValueArgument(0, expression.receiver)
@@ -543,7 +543,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) :
): IrExpression = generateWithStubs(call) { ): IrExpression = generateWithStubs(call) {
if (method.parent !is IrClass) { if (method.parent !is IrClass) {
// Category-provided. // Category-provided.
generationState.llvmImports.add(method.llvmSymbolOrigin) generationState.llvmImports.add(generationState.computeOrigin(method))
} }
this.generateObjCCall( this.generateObjCCall(
@@ -617,7 +617,7 @@ private class InteropLoweringPart1(val generationState: NativeGenerationState) :
// handled in CodeGeneratorVisitor.callVirtual. // handled in CodeGeneratorVisitor.callVirtual.
val useKotlinDispatch = isInteropStubsFile && val useKotlinDispatch = isInteropStubsFile &&
(builder.scope.scopeOwnerSymbol.owner as? IrAnnotationContainer) (builder.scope.scopeOwnerSymbol.owner as? IrAnnotationContainer)
?.hasAnnotation(FqName("kotlin.native.internal.ExportForCppRuntime")) == true ?.hasAnnotation(RuntimeNames.exportForCppRuntime) == true
if (!useKotlinDispatch) { if (!useKotlinDispatch) {
val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) } val arguments = callee.valueParameters.map { expression.getValueArgument(it.index) }
@@ -1011,7 +1011,7 @@ private class InteropTransformer(
private fun generateCCall(expression: IrCall): IrExpression { private fun generateCCall(expression: IrCall): IrExpression {
val function = expression.symbol.owner val function = expression.symbol.owner
generationState.llvmImports.add(function.llvmSymbolOrigin) generationState.llvmImports.add(generationState.computeOrigin(function))
val exceptionMode = ForeignExceptionMode.byValue( val exceptionMode = ForeignExceptionMode.byValue(
function.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey) function.konanLibrary?.manifestProperties?.getProperty(ForeignExceptionMode.manifestKey)
) )
@@ -23,14 +23,17 @@ import org.jetbrains.kotlin.backend.common.serialization.*
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinaryNameAndType import org.jetbrains.kotlin.backend.common.serialization.encodings.BinaryNameAndType
import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData import org.jetbrains.kotlin.backend.common.serialization.encodings.BinarySymbolData
import org.jetbrains.kotlin.backend.common.serialization.encodings.FunctionFlags import org.jetbrains.kotlin.backend.common.serialization.encodings.FunctionFlags
import org.jetbrains.kotlin.backend.common.serialization.isForwardDeclarationModule
import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport import org.jetbrains.kotlin.backend.common.serialization.linkerissues.UserVisibleIrModulesSupport
import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.backend.konan.*
import org.jetbrains.kotlin.backend.konan.descriptors.*
import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder import org.jetbrains.kotlin.backend.konan.descriptors.ClassLayoutBuilder
import org.jetbrains.kotlin.backend.konan.descriptors.findPackage import org.jetbrains.kotlin.backend.konan.descriptors.findPackage
import org.jetbrains.kotlin.backend.konan.descriptors.isInteropLibrary
import org.jetbrains.kotlin.backend.konan.descriptors.toFieldInfo import org.jetbrains.kotlin.backend.konan.descriptors.toFieldInfo
import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs import org.jetbrains.kotlin.backend.konan.ir.interop.IrProviderForCEnumAndCStructStubs
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib import org.jetbrains.kotlin.descriptors.konan.isNativeStdlib
import org.jetbrains.kotlin.ir.IrBuiltIns import org.jetbrains.kotlin.ir.IrBuiltIns
import org.jetbrains.kotlin.ir.builders.TranslationPluginContext import org.jetbrains.kotlin.ir.builders.TranslationPluginContext
@@ -47,8 +50,6 @@ import org.jetbrains.kotlin.ir.visitors.IrElementTransformer
import org.jetbrains.kotlin.ir.visitors.IrElementVisitor import org.jetbrains.kotlin.ir.visitors.IrElementVisitor
import org.jetbrains.kotlin.library.KotlinAbiVersion import org.jetbrains.kotlin.library.KotlinAbiVersion
import org.jetbrains.kotlin.library.KotlinLibrary import org.jetbrains.kotlin.library.KotlinLibrary
import org.jetbrains.kotlin.library.metadata.DeserializedKlibModuleOrigin
import org.jetbrains.kotlin.library.metadata.klibModuleOrigin
import org.jetbrains.kotlin.name.ClassId import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
@@ -196,6 +197,27 @@ internal object ClassFieldsSerializer {
} }
} }
class SerializedEagerInitializedFile(val file: Int)
internal object EagerInitializedPropertySerializer {
fun serialize(properties: List<SerializedEagerInitializedFile>): ByteArray {
val size = properties.sumOf { Int.SIZE_BYTES }
val stream = ByteArrayStream(ByteArray(size))
properties.forEach {
stream.writeInt(it.file)
}
return stream.buf
}
fun deserializeTo(data: ByteArray, result: MutableList<SerializedEagerInitializedFile>) {
val stream = ByteArrayStream(data)
while (stream.hasData()) {
val file = stream.readInt()
result.add(SerializedEagerInitializedFile(file))
}
}
}
internal fun ProtoClass.findClass(irClass: IrClass, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoClass { internal fun ProtoClass.findClass(irClass: IrClass, fileReader: IrLibraryFile, symbolDeserializer: IrSymbolDeserializer): ProtoClass {
val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}") val signature = irClass.symbol.signature ?: error("No signature for ${irClass.render()}")
var result: ProtoClass? = null var result: ProtoClass? = null
@@ -400,9 +422,7 @@ internal class KonanIrLinker(
is IrExternalPackageFragment -> { is IrExternalPackageFragment -> {
val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration val moduleDescriptor = packageFragment.packageFragmentDescriptor.containingDeclaration
val moduleDeserializer = moduleDeserializers[moduleDescriptor] ?: error("No module deserializer for $moduleDescriptor") val moduleDeserializer = moduleDeserializers[moduleDescriptor] ?: error("No module deserializer for $moduleDescriptor")
val descriptor = declaration.descriptor moduleDeserializer.getFileNameOf(declaration)
val idSig = moduleDeserializer.descriptorSignatures[descriptor] ?: error("No signature for $descriptor")
idSig.topLevelSignature().fileSignature()?.fileName ?: error("No file for $idSig")
} }
else -> error("Unknown package fragment kind ${packageFragment::class.java}") else -> error("Unknown package fragment kind ${packageFragment::class.java}")
@@ -443,6 +463,33 @@ internal class KonanIrLinker(
) { ) {
override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns) override val moduleFragment: IrModuleFragment = KonanIrModuleFragmentImpl(moduleDescriptor, builtIns)
val files by lazy { fileDeserializationStates.map { it.file } }
private val fileToFileDeserializationState by lazy { fileDeserializationStates.associateBy { it.file } }
private val idSignatureToFile by lazy {
buildMap {
fileDeserializationStates.forEach { fileDeserializationState ->
fileDeserializationState.fileDeserializer.reversedSignatureIndex.keys.forEach { idSig ->
put(idSig, fileDeserializationState.file)
}
}
}
}
fun getFileNameOf(declaration: IrDeclaration): String {
fun IrDeclaration.getSignature() = symbol.signature ?: descriptorSignatures[descriptor]
val idSig = declaration.getSignature()
?: (declaration.parent as? IrDeclaration)?.getSignature()
?: ((declaration as? IrAttributeContainer)?.attributeOwnerId as? IrDeclaration)?.getSignature()
?: error("Can't find signature of ${declaration.render()}")
val topLevelIdSig = idSig.topLevelSignature()
return topLevelIdSig.fileSignature()?.fileName
?: idSignatureToFile[topLevelIdSig]?.path
?: error("No file for $idSig")
}
fun buildInlineFunctionReference(irFunction: IrFunction): SerializedInlineFunctionReference { fun buildInlineFunctionReference(irFunction: IrFunction): SerializedInlineFunctionReference {
val signature = irFunction.symbol.signature val signature = irFunction.symbol.signature
?: error("No signature for ${irFunction.render()}") ?: error("No signature for ${irFunction.render()}")
@@ -598,6 +645,12 @@ internal class KonanIrLinker(
}) })
} }
fun buildEagerInitializedFile(irFile: IrFile): SerializedEagerInitializedFile {
val fileDeserializationState = fileToFileDeserializationState[irFile]
?: error("No file deserializer for ${irFile.render()}")
return SerializedEagerInitializedFile(fileDeserializationState.fileIndex)
}
private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinderImpl( private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinderImpl(
moduleDescriptor, KonanManglerDesc, moduleDescriptor, KonanManglerDesc,
DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY DescriptorByIdSignatureFinderImpl.LookupMode.MODULE_ONLY
@@ -807,6 +860,13 @@ internal class KonanIrLinker(
} }
} }
val eagerInitializedFiles by lazy {
val cache = cachedLibraries.getLibraryCache(klib)!! // ?: error("No cache for ${klib.libraryName}") // KT-54668
cache.serializedEagerInitializedFiles
.map { fileDeserializationStates[it.file].file }
.distinct()
}
val sortedFileIds by lazy { val sortedFileIds by lazy {
fileDeserializationStates fileDeserializationStates
.sortedBy { it.file.fileEntry.name } .sortedBy { it.file.fileEntry.name }
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package kotlin.native.internal
//
// NOTE: THIS FILE IS AUTO-GENERATED, DO NOT EDIT
//
// This is a special fictitious file, mirroring native runtime.
// During cache building for stdlib, the compiler will link native runtime to the bitcode this file
// will be compiled to. The bitcode dependencies of this file will be generated by the compiler,
// containing all the files with either ExportTypeInfo or ExportForCppRuntime annotations.
//