KT-63264: Introduce a new ExportedBridge annotation

Swift Export Frontend generates a Kotlin file/library that contains a
set of simple bridging functions that connect Swift wrappers to their
Kotlin counterparts.
There are certain requirements for these wrappers:
1. They should not be DCEd when compiling a binary library.
In other words, these functions are roots.
2. They should provide a stable simple binary name.
3. Their signatures should be much simpler and restricted comparing to
other Kotlin functions.

Altogether, these requirements should be covered by introducing the new
ExportedBridge annotation.

Note: Frontend checks of ExportedBridge functions are not implemented
yet.
This commit is contained in:
Sergey Bogolepov
2023-11-25 11:58:08 +02:00
committed by Space Team
parent 8be547b834
commit ce4eecebf4
7 changed files with 30 additions and 5 deletions
@@ -11,6 +11,7 @@ object NativeRuntimeNames {
object Annotations { object Annotations {
val symbolNameClassId = ClassId(kotlinNativePackage, Name.identifier("SymbolName")) val symbolNameClassId = ClassId(kotlinNativePackage, Name.identifier("SymbolName"))
val cNameClassId = ClassId(kotlinNativePackage, Name.identifier("CName")) val cNameClassId = ClassId(kotlinNativePackage, Name.identifier("CName"))
val exportedBridgeClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("ExportedBridge"))
val exportForCppRuntimeClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("ExportForCppRuntime")) val exportForCppRuntimeClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("ExportForCppRuntime"))
val exportForCompilerClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("ExportForCompiler")) val exportForCompilerClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("ExportForCompiler"))
val gcUnsafeCallClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("GCUnsafeCall")) val gcUnsafeCallClassId = ClassId(kotlinNativeInternalPackage, Name.identifier("GCUnsafeCall"))
@@ -8,6 +8,7 @@ object RuntimeNames {
val cnameAnnotation = NativeRuntimeNames.Annotations.cNameClassId.asSingleFqName() val cnameAnnotation = NativeRuntimeNames.Annotations.cNameClassId.asSingleFqName()
val frozenAnnotation = FqName("kotlin.native.internal.Frozen") val frozenAnnotation = FqName("kotlin.native.internal.Frozen")
val exportForCppRuntime = NativeRuntimeNames.Annotations.exportForCppRuntimeClassId.asSingleFqName() val exportForCppRuntime = NativeRuntimeNames.Annotations.exportForCppRuntimeClassId.asSingleFqName()
val exportedBridge = NativeRuntimeNames.Annotations.exportedBridgeClassId.asSingleFqName()
val exportForCompilerAnnotation = NativeRuntimeNames.Annotations.exportForCompilerClassId.asSingleFqName() val exportForCompilerAnnotation = NativeRuntimeNames.Annotations.exportForCompilerClassId.asSingleFqName()
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")
@@ -6,19 +6,21 @@
package org.jetbrains.kotlin.backend.konan.llvm package org.jetbrains.kotlin.backend.konan.llvm
import llvm.LLVMTypeRef import llvm.LLVMTypeRef
import org.jetbrains.kotlin.backend.common.serialization.mangle.MangleConstant
import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType import org.jetbrains.kotlin.backend.common.serialization.mangle.SpecialDeclarationType
import org.jetbrains.kotlin.backend.konan.RuntimeNames import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow import org.jetbrains.kotlin.backend.konan.descriptors.externalSymbolOrThrow
import org.jetbrains.kotlin.ir.util.getAnnotationStringValue
import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract import org.jetbrains.kotlin.backend.konan.descriptors.isAbstract
import org.jetbrains.kotlin.backend.konan.ir.isUnit import org.jetbrains.kotlin.backend.konan.ir.isUnit
import org.jetbrains.kotlin.backend.konan.serialization.AbstractKonanIrMangler
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrField
import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.objcinterop.isExternalObjCClass import org.jetbrains.kotlin.ir.objcinterop.isExternalObjCClass
import org.jetbrains.kotlin.ir.objcinterop.isKotlinObjCClass import org.jetbrains.kotlin.ir.objcinterop.isKotlinObjCClass
import org.jetbrains.kotlin.backend.konan.serialization.AbstractKonanIrMangler
import org.jetbrains.kotlin.ir.declarations.*
import org.jetbrains.kotlin.ir.util.findAnnotation import org.jetbrains.kotlin.ir.util.findAnnotation
import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization import org.jetbrains.kotlin.ir.util.fqNameForIrSerialization
import org.jetbrains.kotlin.ir.util.getAnnotationStringValue
import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.util.render
import org.jetbrains.kotlin.konan.library.KonanLibrary import org.jetbrains.kotlin.konan.library.KonanLibrary
import org.jetbrains.kotlin.library.uniqueName import org.jetbrains.kotlin.library.uniqueName
@@ -62,6 +64,10 @@ object KonanBinaryInterface {
private fun withPrefix(prefix: String, mangle: String) = "$prefix:$mangle" private fun withPrefix(prefix: String, mangle: String) = "$prefix:$mangle"
private fun IrFunction.findManglingAnnotation() =
this.annotations.findAnnotation(RuntimeNames.exportForCppRuntime)
?: this.annotations.findAnnotation(RuntimeNames.exportedBridge)
private fun IrFunction.funSymbolNameImpl(containerName: String?): String { private fun IrFunction.funSymbolNameImpl(containerName: String?): String {
if (isExternal) { if (isExternal) {
this.externalSymbolOrThrow()?.let { this.externalSymbolOrThrow()?.let {
@@ -69,7 +75,7 @@ object KonanBinaryInterface {
} }
} }
this.annotations.findAnnotation(RuntimeNames.exportForCppRuntime)?.let { this.findManglingAnnotation()?.let {
val name = it.getAnnotationStringValue() ?: this.name.asString() val name = it.getAnnotationStringValue() ?: this.name.asString()
return name // no wrapping currently required return name // no wrapping currently required
} }
@@ -114,6 +114,8 @@ internal inline fun generateFunction(
val llvmFunction = codegen.llvmFunction(function) val llvmFunction = codegen.llvmFunction(function)
val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE val isCToKotlinBridge = function.origin == CBridgeOrigin.C_TO_KOTLIN_BRIDGE
// TODO: Alternative approach: lowering that changes origin of such functions to C_TO_KOTLIN_BRIDGE?
|| function.hasAnnotation(RuntimeNames.exportedBridge)
val functionGenerationContext = DefaultFunctionGenerationContext( val functionGenerationContext = DefaultFunctionGenerationContext(
llvmFunction, llvmFunction,
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.backend.konan.llvm package org.jetbrains.kotlin.backend.konan.llvm
import org.jetbrains.kotlin.backend.konan.RuntimeNames
import org.jetbrains.kotlin.ir.util.getAnnotationStringValue import org.jetbrains.kotlin.ir.util.getAnnotationStringValue
import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrFunction
import org.jetbrains.kotlin.ir.util.findAnnotation import org.jetbrains.kotlin.ir.util.findAnnotation
@@ -18,5 +19,6 @@ internal fun IrFunction.retainAnnotation(target: KonanTarget): Boolean {
if (this.annotations.findAnnotation(retainAnnotationName) != null) return true if (this.annotations.findAnnotation(retainAnnotationName) != null) return true
val forTarget = this.annotations.findAnnotation(retainForTargetAnnotationName) val forTarget = this.annotations.findAnnotation(retainForTargetAnnotationName)
if (forTarget != null && forTarget.getAnnotationStringValue() == target.name) return true if (forTarget != null && forTarget.getAnnotationStringValue() == target.name) return true
if (this.annotations.findAnnotation(RuntimeNames.exportedBridge) != null) return true
return false return false
} }
@@ -603,6 +603,7 @@ internal object DataFlowIR {
if (returnsNothing) if (returnsNothing)
attributes = attributes or FunctionAttributes.RETURNS_NOTHING attributes = attributes or FunctionAttributes.RETURNS_NOTHING
if (it.hasAnnotation(RuntimeNames.exportForCppRuntime) if (it.hasAnnotation(RuntimeNames.exportForCppRuntime)
|| it.hasAnnotation(RuntimeNames.exportedBridge)
|| it.getExternalObjCMethodInfo() != null // TODO-DCE-OBJC-INIT || it.getExternalObjCMethodInfo() != null // TODO-DCE-OBJC-INIT
|| it.hasAnnotation(RuntimeNames.objCMethodImp)) { || it.hasAnnotation(RuntimeNames.objCMethodImp)) {
attributes = attributes or FunctionAttributes.EXPLICITLY_EXPORTED attributes = attributes or FunctionAttributes.EXPLICITLY_EXPORTED
@@ -5,6 +5,8 @@
package kotlin.native.internal package kotlin.native.internal
import kotlin.experimental.ExperimentalNativeApi
/** /**
* Makes this function to be possible to call by given name from C++ part of runtime using C ABI. * Makes this function to be possible to call by given name from C++ part of runtime using C ABI.
* The parameters are mapped in an implementation-dependent manner. * The parameters are mapped in an implementation-dependent manner.
@@ -180,3 +182,13 @@ internal annotation class InternalForKotlinNativeTests
@InternalForKotlinNativeTests @InternalForKotlinNativeTests
@Target(AnnotationTarget.FILE) @Target(AnnotationTarget.FILE)
public annotation class ReflectionPackageName(val name: String) public annotation class ReflectionPackageName(val name: String)
/**
* Indicates that the marked function is an exported bridge between Kotlin and the platform.
* This annotation prevents the function from being removed by DCE
* and specifies a stable [bridgeName] for the function symbol.
*/
@Target(AnnotationTarget.FUNCTION)
@Retention(value = AnnotationRetention.BINARY)
@ExperimentalNativeApi
public annotation class ExportedBridge(val bridgeName: String)