Refine LLVM bitcode imports detection

Don't link native libraries if external function requires
only library-stored bitcode
This commit is contained in:
Svyatoslav Scherbina
2019-03-14 17:35:27 +03:00
committed by SvyatoslavScherbina
parent f8393819fc
commit 06f8864b69
17 changed files with 76 additions and 26 deletions
@@ -27,7 +27,8 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
stubGenerator.simpleBridgeGenerator.kotlinToNative( stubGenerator.simpleBridgeGenerator.kotlinToNative(
nativeBacked = this, nativeBacked = this,
returnType = BridgedType.NATIVE_PTR, returnType = BridgedType.NATIVE_PTR,
kotlinValues = emptyList() kotlinValues = emptyList(),
independent = false
) { ) {
"&${global.name}" "&${global.name}"
} }
@@ -58,7 +59,8 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
getter = mirror.info.argFromBridged(stubGenerator.simpleBridgeGenerator.kotlinToNative( getter = mirror.info.argFromBridged(stubGenerator.simpleBridgeGenerator.kotlinToNative(
nativeBacked = this, nativeBacked = this,
returnType = mirror.info.bridgedType, returnType = mirror.info.bridgedType,
kotlinValues = emptyList() kotlinValues = emptyList(),
independent = false
) { ) {
mirror.info.cToBridged(expr = global.name) mirror.info.cToBridged(expr = global.name)
}, kotlinScope, nativeBacked = this) }, kotlinScope, nativeBacked = this)
@@ -71,7 +73,8 @@ class GlobalVariableStub(global: GlobalDecl, stubGenerator: StubGenerator) : Kot
stubGenerator.simpleBridgeGenerator.kotlinToNative( stubGenerator.simpleBridgeGenerator.kotlinToNative(
nativeBacked = setterStub, nativeBacked = setterStub,
returnType = BridgedType.VOID, returnType = BridgedType.VOID,
kotlinValues = listOf(bridgedValue) kotlinValues = listOf(bridgedValue),
independent = false
) { nativeValues -> ) { nativeValues ->
out("${global.name} = ${mirror.info.cFromBridged( out("${global.name} = ${mirror.info.cFromBridged(
nativeValues.single(), nativeValues.single(),
@@ -157,6 +157,8 @@ data class KotlinFunctionType(
internal val cnamesStructsPackageName = "cnames.structs" internal val cnamesStructsPackageName = "cnames.structs"
object KotlinTypes { object KotlinTypes {
val independent = Classifier.topLevel("kotlin.native.internal", "Independent")
val boolean by BuiltInType val boolean by BuiltInType
val byte by BuiltInType val byte by BuiltInType
val short by BuiltInType val short by BuiltInType
@@ -32,6 +32,7 @@ interface MappingBridgeGenerator {
nativeBacked: NativeBacked, nativeBacked: NativeBacked,
returnType: Type, returnType: Type,
kotlinValues: List<TypedKotlinValue>, kotlinValues: List<TypedKotlinValue>,
independent: Boolean,
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
): KotlinExpression ): KotlinExpression
@@ -35,6 +35,7 @@ class MappingBridgeGeneratorImpl(
nativeBacked: NativeBacked, nativeBacked: NativeBacked,
returnType: Type, returnType: Type,
kotlinValues: List<TypedKotlinValue>, kotlinValues: List<TypedKotlinValue>,
independent: Boolean,
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
): KotlinExpression { ): KotlinExpression {
val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>() val bridgeArguments = mutableListOf<BridgeTypedKotlinValue>()
@@ -70,7 +71,7 @@ class MappingBridgeGeneratorImpl(
} }
val callExpr = simpleBridgeGenerator.kotlinToNative( val callExpr = simpleBridgeGenerator.kotlinToNative(
nativeBacked, bridgeReturnType, bridgeArguments nativeBacked, bridgeReturnType, bridgeArguments, independent
) { bridgeNativeValues -> ) { bridgeNativeValues ->
val nativeValues = mutableListOf<String>() val nativeValues = mutableListOf<String>()
@@ -307,7 +307,8 @@ sealed class TypeInfo {
type.returnType, type.returnType,
type.parameterTypes.mapIndexed { index, it -> type.parameterTypes.mapIndexed { index, it ->
TypedKotlinValue(it, "p$index") TypedKotlinValue(it, "p$index")
} + TypedKotlinValue(PointerType(VoidType), "interpretCPointer<COpaque>($kniBlockPtr)") } + TypedKotlinValue(PointerType(VoidType), "interpretCPointer<COpaque>($kniBlockPtr)"),
independent = true
) { nativeValues -> ) { nativeValues ->
val type = type val type = type
@@ -226,7 +226,11 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
bodyGenerator, bodyGenerator,
this@ObjCMethodStub, this@ObjCMethodStub,
returnType, returnType,
nativeBridgeArguments nativeBridgeArguments,
independent = when (container) {
is ObjCClassOrProtocol -> true // Every proper instance has this method in its method table.
is ObjCCategory -> false // Method is contributed by native dependency.
}
) { nativeValues -> ) { nativeValues ->
val messengerParameterTypes = mutableListOf<String>() val messengerParameterTypes = mutableListOf<String>()
messengerParameterTypes.add("void*") messengerParameterTypes.add("void*")
@@ -60,6 +60,7 @@ interface SimpleBridgeGenerator {
nativeBacked: NativeBacked, nativeBacked: NativeBacked,
returnType: BridgedType, returnType: BridgedType,
kotlinValues: List<BridgeTypedKotlinValue>, kotlinValues: List<BridgeTypedKotlinValue>,
independent: Boolean,
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
): KotlinExpression ): KotlinExpression
@@ -71,7 +71,8 @@ class SimpleBridgeGeneratorImpl(
nativeBacked: NativeBacked, nativeBacked: NativeBacked,
returnType: BridgedType, returnType: BridgedType,
kotlinValues: List<BridgeTypedKotlinValue>, kotlinValues: List<BridgeTypedKotlinValue>,
block: NativeCodeBuilder.(arguments: List<NativeExpression>) -> NativeExpression independent: Boolean,
block: NativeCodeBuilder.(nativeValues: List<NativeExpression>) -> NativeExpression
): KotlinExpression { ): KotlinExpression {
val kotlinLines = mutableListOf<String>() val kotlinLines = mutableListOf<String>()
@@ -116,6 +117,7 @@ class SimpleBridgeGeneratorImpl(
} }
KotlinPlatform.NATIVE -> { KotlinPlatform.NATIVE -> {
val functionName = pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + "_$kotlinFunctionName" val functionName = pkgName.replace(INVALID_CLANG_IDENTIFIER_REGEX, "_") + "_$kotlinFunctionName"
if (independent) kotlinLines.add("@" + topLevelKotlinScope.reference(KotlinTypes.independent))
kotlinLines.add("@SymbolName(${functionName.quoteAsKotlinLiteral()})") kotlinLines.add("@SymbolName(${functionName.quoteAsKotlinLiteral()})")
"$cReturnType $functionName ($joinedCParameters)" "$cReturnType $functionName ($joinedCParameters)"
} }
@@ -660,7 +660,8 @@ class StubGenerator(
bodyGenerator, bodyGenerator,
this, this,
func.returnType, func.returnType,
bridgeArguments bridgeArguments,
independent = false
) { nativeValues -> ) { nativeValues ->
"${func.name}(${nativeValues.joinToString()})" "${func.name}(${nativeValues.joinToString()})"
} }
@@ -40,7 +40,10 @@ internal class LinkStage(val context: Context) {
private val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false private val nomain = config.get(KonanConfigKeys.NOMAIN) ?: false
private val emitted = context.bitcodeFileName private val emitted = context.bitcodeFileName
private val libraries = context.llvm.librariesToLink
private val bitcodeLibraries = context.llvm.bitcodeToLink
private val nativeDependencies = context.llvm.nativeDependenciesToLink
private fun MutableList<String>.addNonEmpty(elements: List<String>) { private fun MutableList<String>.addNonEmpty(elements: List<String>) {
addAll(elements.filter { !it.isEmpty() }) addAll(elements.filter { !it.isEmpty() })
} }
@@ -221,7 +224,7 @@ internal class LinkStage(val context: Context) {
fun makeObjectFiles() { fun makeObjectFiles() {
val bitcodeFiles = listOf(emitted) + val bitcodeFiles = listOf(emitted) +
libraries.map { it.bitcodePaths }.flatten().filter { it.isBitcode } bitcodeLibraries.map { it.bitcodePaths }.flatten().filter { it.isBitcode }
objectFiles.add(when (platform.configurables) { objectFiles.add(when (platform.configurables) {
is WasmConfigurables is WasmConfigurables
@@ -235,10 +238,10 @@ internal class LinkStage(val context: Context) {
fun linkStage() { fun linkStage() {
val includedBinaries = val includedBinaries =
libraries.map { it.includedPaths }.flatten() nativeDependencies.map { it.includedPaths }.flatten()
val libraryProvidedLinkerFlags = val libraryProvidedLinkerFlags =
libraries.map { it.linkerOpts }.flatten() nativeDependencies.map { it.linkerOpts }.flatten()
link(objectFiles, includedBinaries, libraryProvidedLinkerFlags) link(objectFiles, includedBinaries, libraryProvidedLinkerFlags)
} }
@@ -8,4 +8,5 @@ object RuntimeNames {
val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler") val exportForCompilerAnnotation = FqName("kotlin.native.internal.ExportForCompiler")
val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo") val exportTypeInfoAnnotation = FqName("kotlin.native.internal.ExportTypeInfo")
val cCall = FqName("kotlinx.cinterop.internal.CCall") val cCall = FqName("kotlinx.cinterop.internal.CCall")
val independent = FqName("kotlin.native.internal.Independent")
} }
@@ -296,8 +296,13 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
return LLVMAddFunction(llvmModule, "llvm.memcpy.p0i8.p0i8.i32", functionType)!! return LLVMAddFunction(llvmModule, "llvm.memcpy.p0i8.p0i8.i32", functionType)!!
} }
internal fun externalFunction(name: String, type: LLVMTypeRef, origin: CompiledKonanModuleOrigin): LLVMValueRef { internal fun externalFunction(
this.imports.add(origin) name: String,
type: LLVMTypeRef,
origin: CompiledKonanModuleOrigin,
independent: Boolean = false
): LLVMValueRef {
this.imports.add(origin, onlyBitcode = independent)
val found = LLVMGetNamedFunction(llvmModule, name) val found = LLVMGetNamedFunction(llvmModule, name)
if (found != null) { if (found != null) {
@@ -336,11 +341,12 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
class ImportsImpl(private val context: Context) : LlvmImports { class ImportsImpl(private val context: Context) : LlvmImports {
private val usedLibraries = mutableSetOf<KonanLibrary>() private val usedBitcode = mutableSetOf<KonanLibrary>()
private val usedNativeDependencies = mutableSetOf<KonanLibrary>()
private val allLibraries by lazy { context.librariesWithDependencies.toSet() } private val allLibraries by lazy { context.librariesWithDependencies.toSet() }
override fun add(origin: CompiledKonanModuleOrigin) { override fun add(origin: CompiledKonanModuleOrigin, onlyBitcode: Boolean) {
val library = when (origin) { val library = when (origin) {
CurrentKonanModuleOrigin -> return CurrentKonanModuleOrigin -> return
is DeserializedKonanModuleOrigin -> origin.library is DeserializedKonanModuleOrigin -> origin.library
@@ -350,16 +356,28 @@ internal class Llvm(val context: Context, val llvmModule: LLVMModuleRef) {
error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}") error("Library (${library.libraryName}) is used but not requested.\nRequested libraries: ${allLibraries.joinToString { it.libraryName }}")
} }
usedLibraries.add(library) usedBitcode.add(library)
if (!onlyBitcode) {
usedNativeDependencies.add(library)
}
} }
override fun isImported(library: KonanLibrary): Boolean = library in usedLibraries override fun bitcodeIsUsed(library: KonanLibrary) = library in usedBitcode
override fun nativeDependenciesAreUsed(library: KonanLibrary) = library in usedNativeDependencies
} }
val librariesToLink: List<KonanLibrary> by lazy { val nativeDependenciesToLink: List<KonanLibrary> by lazy {
context.config.resolvedLibraries context.config.resolvedLibraries
.filterRoots { (!it.isDefault && !context.config.purgeUserLibs) || imports.isImported(it.library) }
.getFullList(TopologicalLibraryOrder) .getFullList(TopologicalLibraryOrder)
.filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.nativeDependenciesAreUsed(it) }
}
val bitcodeToLink: List<KonanLibrary> by lazy {
context.config.resolvedLibraries
.getFullList(TopologicalLibraryOrder)
.filter { (!it.isDefault && !context.config.purgeUserLibs) || imports.bitcodeIsUsed(it) }
} }
val staticData = StaticData(context) val staticData = StaticData(context)
@@ -15,8 +15,9 @@ import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.module
internal interface LlvmImports { internal interface LlvmImports {
fun add(origin: CompiledKonanModuleOrigin) fun add(origin: CompiledKonanModuleOrigin, onlyBitcode: Boolean = false)
fun isImported(library: KonanLibrary): Boolean fun bitcodeIsUsed(library: KonanLibrary): Boolean
fun nativeDependenciesAreUsed(library: KonanLibrary): Boolean
} }
internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKonanModuleOrigin internal val DeclarationDescriptor.llvmSymbolOrigin: CompiledKonanModuleOrigin
@@ -2123,7 +2123,8 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
val protocolGetter = context.llvm.externalFunction( val protocolGetter = context.llvm.externalFunction(
protocolGetterName, protocolGetterName,
functionType(int8TypePtr, false), functionType(int8TypePtr, false),
irClass.llvmSymbolOrigin irClass.llvmSymbolOrigin,
independent = true // Protocol is header-only declaration.
) )
return call(protocolGetter, emptyList()) return call(protocolGetter, emptyList())
@@ -120,7 +120,8 @@ internal class KotlinObjCClassInfoGenerator(override val context: Context) : Con
context.llvm.externalFunction( context.llvm.externalFunction(
imp, imp,
functionType(voidType), functionType(voidType),
origin = info.bridge.llvmSymbolOrigin origin = info.bridge.llvmSymbolOrigin,
independent = true
) )
) )
} }
@@ -397,7 +397,8 @@ private class DeclarationsGeneratorVisitor(override val context: Context) :
context.llvm.externalFunction(declaration.symbolName, llvmFunctionType, context.llvm.externalFunction(declaration.symbolName, llvmFunctionType,
// Assume that `external fun` is defined in native libs attached to this module: // Assume that `external fun` is defined in native libs attached to this module:
origin = declaration.llvmSymbolOrigin origin = declaration.llvmSymbolOrigin,
independent = declaration.hasAnnotation(RuntimeNames.independent)
) )
} else { } else {
val symbolName = if (declaration.isExported()) { val symbolName = if (declaration.isExported()) {
@@ -105,3 +105,11 @@ internal annotation class PointsTo(vararg val onWhom: Int)
@Target(AnnotationTarget.FUNCTION) @Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY) @Retention(AnnotationRetention.BINARY)
internal annotation class TypedIntrinsic(val kind: String) internal annotation class TypedIntrinsic(val kind: String)
/**
* Indicates that `@SymbolName external` function is implemented in library-stored bitcode
* and doesn't have native dependencies.
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(AnnotationRetention.BINARY)
annotation class Independent