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
|
||||
|
||||
@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() {
|
||||
@SymbolName("Konan_ObjCInterop_getWeakReference")
|
||||
|
||||
+45
-5
@@ -16,6 +16,7 @@
|
||||
|
||||
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.indexer.*
|
||||
|
||||
@@ -155,7 +156,8 @@ class ObjCMethodStub(private val stubGenerator: StubGenerator,
|
||||
|
||||
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))
|
||||
nativeBridgeArguments.add(TypedKotlinValue(voidPtr, "$messengerGetter($kniSuperClassParameter)"))
|
||||
@@ -277,14 +279,52 @@ private val ObjCContainer.classOrProtocol: ObjCClassOrProtocol
|
||||
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()
|
||||
return when (unwrappedType) {
|
||||
is RecordType -> unwrappedType.decl.def!!.size > 16 || this.hasUnalignedMembers()
|
||||
else -> false
|
||||
return when (target) {
|
||||
KonanTarget.IOS_ARM64 ->
|
||||
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) {
|
||||
is Typedef -> this.def.aliased.hasUnalignedMembers()
|
||||
is RecordType -> this.decl.def!!.let { def ->
|
||||
|
||||
+3
-1
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.native.interop.gen.jvm
|
||||
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.native.interop.indexer.NativeLibrary
|
||||
|
||||
/**
|
||||
@@ -29,5 +30,6 @@ class InteropConfiguration(
|
||||
val nonStrictEnums: Set<String>,
|
||||
val noStringConversion: Set<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 targetManager = platformManager.targetManager(userProvidedTargetName)
|
||||
private val host = HostManager.host
|
||||
private val target = targetManager.target
|
||||
val target = targetManager.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(),
|
||||
noStringConversion = def.config.noStringConversion.toSet(),
|
||||
exportForwardDeclarations = def.config.exportForwardDeclarations,
|
||||
disableDesignatedInitializerChecks = def.config.disableDesignatedInitializerChecks
|
||||
disableDesignatedInitializerChecks = def.config.disableDesignatedInitializerChecks,
|
||||
target = tool.target
|
||||
)
|
||||
|
||||
val nativeIndex = buildNativeIndex(library)
|
||||
|
||||
+1
-1
@@ -173,7 +173,7 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
val getObjCReceiverOrSuper = packageScope.getContributedFunctions("getReceiverOrSuper").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 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.acceptVoid
|
||||
import org.jetbrains.kotlin.konan.target.CompilerOutputKind
|
||||
import org.jetbrains.kotlin.konan.target.KonanTarget
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
|
||||
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 -> {
|
||||
genGetObjCMessenger(args, isLU = false)
|
||||
genGetObjCMessenger(args, isStret = false)
|
||||
}
|
||||
interop.getObjCMessengerLU -> {
|
||||
genGetObjCMessenger(args, isLU = true)
|
||||
interop.getObjCMessengerStret -> {
|
||||
genGetObjCMessenger(args, isStret = true)
|
||||
}
|
||||
|
||||
interop.readBits -> genReadBits(args)
|
||||
@@ -2327,25 +2326,9 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
|
||||
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
|
||||
|
||||
// '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 functionType = functionType(int8TypePtr, true, int8TypePtr, int8TypePtr)
|
||||
|
||||
Reference in New Issue
Block a user