Fix iOS ARM32 ABI support in Objective-C interop
This commit is contained in:
committed by
SvyatoslavScherbina
parent
d3e4531602
commit
8591f13b94
@@ -148,7 +148,7 @@ private fun allocObjCObject(clazz: NativePtr): NativePtr {
|
|||||||
private external fun <T : ObjCObject> getObjCClass(): NativePtr
|
private external fun <T : ObjCObject> getObjCClass(): NativePtr
|
||||||
|
|
||||||
@konan.internal.Intrinsic external fun getMessenger(superClass: NativePtr): COpaquePointer?
|
@konan.internal.Intrinsic external fun getMessenger(superClass: NativePtr): COpaquePointer?
|
||||||
@konan.internal.Intrinsic external fun getMessengerLU(superClass: NativePtr): COpaquePointer?
|
@konan.internal.Intrinsic external fun getMessengerStret(superClass: NativePtr): COpaquePointer?
|
||||||
|
|
||||||
internal class ObjCWeakReferenceImpl : konan.ref.WeakReferenceImpl() {
|
internal class ObjCWeakReferenceImpl : konan.ref.WeakReferenceImpl() {
|
||||||
@SymbolName("Konan_ObjCInterop_getWeakReference")
|
@SymbolName("Konan_ObjCInterop_getWeakReference")
|
||||||
|
|||||||
+45
-5
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen
|
package org.jetbrains.kotlin.native.interop.gen
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.native.interop.gen.jvm.StubGenerator
|
import org.jetbrains.kotlin.native.interop.gen.jvm.StubGenerator
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.*
|
import org.jetbrains.kotlin.native.interop.indexer.*
|
||||||
|
|
||||||
@@ -155,7 +156,8 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
|||||||
|
|
||||||
val returnType = method.getReturnType(container.classOrProtocol)
|
val returnType = method.getReturnType(container.classOrProtocol)
|
||||||
|
|
||||||
val messengerGetter = if (returnType.isLargeOrUnaligned()) "getMessengerLU" else "getMessenger"
|
val messengerGetter =
|
||||||
|
if (returnType.isStret(stubGenerator.configuration.target)) "getMessengerStret" else "getMessenger"
|
||||||
|
|
||||||
kotlinObjCBridgeParameters.add(KotlinParameter(kniSuperClassParameter, KotlinTypes.nativePtr))
|
kotlinObjCBridgeParameters.add(KotlinParameter(kniSuperClassParameter, KotlinTypes.nativePtr))
|
||||||
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, "$messengerGetter($kniSuperClassParameter)"))
|
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, "$messengerGetter($kniSuperClassParameter)"))
|
||||||
@@ -277,14 +279,52 @@ private val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
|||||||
is ObjCCategory -> this.clazz
|
is ObjCCategory -> this.clazz
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Type.isLargeOrUnaligned(): Boolean {
|
/**
|
||||||
|
* objc_msgSend*_stret functions must be used when return value is returned through memory
|
||||||
|
* pointed by implicit argument, which is passed on the register that would otherwise be used for receiver.
|
||||||
|
*
|
||||||
|
* The entire implementation is just the real ABI approximation which is enough for practical cases.
|
||||||
|
*/
|
||||||
|
private fun Type.isStret(target: KonanTarget): Boolean {
|
||||||
val unwrappedType = this.unwrapTypedefs()
|
val unwrappedType = this.unwrapTypedefs()
|
||||||
return when (unwrappedType) {
|
return when (target) {
|
||||||
is RecordType -> unwrappedType.decl.def!!.size > 16 || this.hasUnalignedMembers()
|
KonanTarget.IOS_ARM64 ->
|
||||||
else -> false
|
false // On aarch64 stret is never the case, since an implicit argument gets passed on x8.
|
||||||
|
|
||||||
|
KonanTarget.IOS_X64, KonanTarget.MACOS_X64 -> when (unwrappedType) {
|
||||||
|
is RecordType -> unwrappedType.decl.def!!.size > 16 || this.hasUnalignedMembers()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
KonanTarget.IOS_ARM32 -> {
|
||||||
|
when (unwrappedType) {
|
||||||
|
is RecordType -> !this.isIntegerLikeType()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> error(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun Type.isIntegerLikeType(): Boolean = when (this) {
|
||||||
|
is RecordType -> {
|
||||||
|
val def = this.decl.def
|
||||||
|
if (def == null) {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
def.size <= 4 &&
|
||||||
|
def.bitFields.all { it.type.isIntegerLikeType() } &&
|
||||||
|
def.fields.all { it.offset == 0L && it.type.isIntegerLikeType() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is ObjCPointer, is PointerType, CharType, BoolType -> true
|
||||||
|
is IntegerType -> this.size <= 4
|
||||||
|
is Typedef -> this.def.aliased.isIntegerLikeType()
|
||||||
|
is EnumType -> this.def.baseType.isIntegerLikeType()
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
|
||||||
private fun Type.hasUnalignedMembers(): Boolean = when (this) {
|
private fun Type.hasUnalignedMembers(): Boolean = when (this) {
|
||||||
is Typedef -> this.def.aliased.hasUnalignedMembers()
|
is Typedef -> this.def.aliased.hasUnalignedMembers()
|
||||||
is RecordType -> this.decl.def!!.let { def ->
|
is RecordType -> this.decl.def!!.let { def ->
|
||||||
|
|||||||
+3
-1
@@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||||
|
|
||||||
|
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -29,5 +30,6 @@ class InteropConfiguration(
|
|||||||
val nonStrictEnums: Set<String>,
|
val nonStrictEnums: Set<String>,
|
||||||
val noStringConversion: Set<String>,
|
val noStringConversion: Set<String>,
|
||||||
val exportForwardDeclarations: List<String>,
|
val exportForwardDeclarations: List<String>,
|
||||||
val disableDesignatedInitializerChecks: Boolean
|
val disableDesignatedInitializerChecks: Boolean,
|
||||||
|
val target: KonanTarget
|
||||||
)
|
)
|
||||||
+1
-1
@@ -30,7 +30,7 @@ class ToolConfig(userProvidedTargetName: String?, flavor: KotlinPlatform) {
|
|||||||
private val platformManager = PlatformManager(distribution)
|
private val platformManager = PlatformManager(distribution)
|
||||||
private val targetManager = platformManager.targetManager(userProvidedTargetName)
|
private val targetManager = platformManager.targetManager(userProvidedTargetName)
|
||||||
private val host = HostManager.host
|
private val host = HostManager.host
|
||||||
private val target = targetManager.target
|
val target = targetManager.target
|
||||||
|
|
||||||
private val platform = platformManager.platform(target)
|
private val platform = platformManager.platform(target)
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -277,7 +277,8 @@ private fun processCLib(args: Array<String>): Array<String>? {
|
|||||||
nonStrictEnums = def.config.nonStrictEnums.toSet(),
|
nonStrictEnums = def.config.nonStrictEnums.toSet(),
|
||||||
noStringConversion = def.config.noStringConversion.toSet(),
|
noStringConversion = def.config.noStringConversion.toSet(),
|
||||||
exportForwardDeclarations = def.config.exportForwardDeclarations,
|
exportForwardDeclarations = def.config.exportForwardDeclarations,
|
||||||
disableDesignatedInitializerChecks = def.config.disableDesignatedInitializerChecks
|
disableDesignatedInitializerChecks = def.config.disableDesignatedInitializerChecks,
|
||||||
|
target = tool.target
|
||||||
)
|
)
|
||||||
|
|
||||||
val nativeIndex = buildNativeIndex(library)
|
val nativeIndex = buildNativeIndex(library)
|
||||||
|
|||||||
+1
-1
@@ -173,7 +173,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
|||||||
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").single()
|
||||||
|
|
||||||
val getObjCMessenger = packageScope.getContributedFunctions("getMessenger").single()
|
val getObjCMessenger = packageScope.getContributedFunctions("getMessenger").single()
|
||||||
val getObjCMessengerLU = packageScope.getContributedFunctions("getMessengerLU").single()
|
val getObjCMessengerStret = packageScope.getContributedFunctions("getMessengerStret").single()
|
||||||
|
|
||||||
val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single()
|
val interpretObjCPointerOrNull = packageScope.getContributedFunctions("interpretObjCPointerOrNull").single()
|
||||||
val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single()
|
val interpretObjCPointer = packageScope.getContributedFunctions("interpretObjCPointer").single()
|
||||||
|
|||||||
+4
-21
@@ -42,7 +42,6 @@ import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
|||||||
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
import org.jetbrains.kotlin.ir.visitors.acceptVoid
|
||||||
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.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
|
|
||||||
val IrClassSymbol.objectIsShared get() = owner.origin == DECLARATION_ORIGIN_ENUM
|
val IrClassSymbol.objectIsShared get() = owner.origin == DECLARATION_ORIGIN_ENUM
|
||||||
@@ -2165,10 +2164,10 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
}
|
}
|
||||||
|
|
||||||
interop.getObjCMessenger -> {
|
interop.getObjCMessenger -> {
|
||||||
genGetObjCMessenger(args, isLU = false)
|
genGetObjCMessenger(args, isStret = false)
|
||||||
}
|
}
|
||||||
interop.getObjCMessengerLU -> {
|
interop.getObjCMessengerStret -> {
|
||||||
genGetObjCMessenger(args, isLU = true)
|
genGetObjCMessenger(args, isStret = true)
|
||||||
}
|
}
|
||||||
|
|
||||||
interop.readBits -> genReadBits(args)
|
interop.readBits -> genReadBits(args)
|
||||||
@@ -2327,25 +2326,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
|||||||
return call(protocolGetter, emptyList())
|
return call(protocolGetter, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun genGetObjCMessenger(args: List<LLVMValueRef>, isLU: Boolean): LLVMValueRef {
|
private fun genGetObjCMessenger(args: List<LLVMValueRef>, isStret: Boolean): LLVMValueRef {
|
||||||
val gen = functionGenerationContext
|
val gen = functionGenerationContext
|
||||||
|
|
||||||
// 'LU' means "large or unaligned".
|
|
||||||
|
|
||||||
// objc_msgSend*_stret functions must be used when return value is returned through memory
|
|
||||||
// pointed by implicit argument, which is passed on the register that would otherwise be used for receiver.
|
|
||||||
// On aarch64 it is never the case, since such implicit argument gets passed on x8.
|
|
||||||
// On x86_64 it is the case if the return value takes more than 16 bytes or is the structure with
|
|
||||||
// unaligned fields (there are some complicated exceptions currently ignored). The latter condition
|
|
||||||
// is "encoded" by stub generator by emitting either `getMessenger` or `getMessengerLU` intrinsic call.
|
|
||||||
val isStret = when (context.config.target) {
|
|
||||||
KonanTarget.MACOS_X64, KonanTarget.IOS_X64 -> isLU // x86_64
|
|
||||||
KonanTarget.IOS_ARM64 -> false // aarch64
|
|
||||||
// TODO: what is the correct value?
|
|
||||||
KonanTarget.IOS_ARM32 -> false // armv7
|
|
||||||
else -> TODO()
|
|
||||||
}
|
|
||||||
|
|
||||||
val messengerNameSuffix = if (isStret) "_stret" else ""
|
val messengerNameSuffix = if (isStret) "_stret" else ""
|
||||||
|
|
||||||
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
|
val functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
|
||||||
|
|||||||
Reference in New Issue
Block a user