Fix iOS ARM32 ABI support in Objective-C interop

This commit is contained in:
Svyatoslav Scherbina
2018-06-25 19:18:34 +03:00
committed by SvyatoslavScherbina
parent d3e4531602
commit 8591f13b94
7 changed files with 57 additions and 31 deletions
@@ -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")
@@ -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 ->
@@ -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
)
@@ -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)
@@ -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)
@@ -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()
@@ -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)