From 8591f13b940b0e151fec1f5c14aafdc6e9be1b97 Mon Sep 17 00:00:00 2001 From: Svyatoslav Scherbina Date: Mon, 25 Jun 2018 19:18:34 +0300 Subject: [PATCH] Fix iOS ARM32 ABI support in Objective-C interop --- .../kotlin/kotlinx/cinterop/ObjectiveCImpl.kt | 2 +- .../kotlin/native/interop/gen/ObjCStubs.kt | 50 +++++++++++++++++-- .../interop/gen/jvm/InteropConfiguration.kt | 4 +- .../native/interop/gen/jvm/ToolConfig.kt | 2 +- .../kotlin/native/interop/gen/jvm/main.kt | 3 +- .../kotlin/backend/konan/InteropUtils.kt | 2 +- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 25 ++-------- 7 files changed, 57 insertions(+), 31 deletions(-) diff --git a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt index 4f59b738b4f..0c8d10d7eb8 100644 --- a/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt +++ b/Interop/Runtime/src/native/kotlin/kotlinx/cinterop/ObjectiveCImpl.kt @@ -148,7 +148,7 @@ private fun allocObjCObject(clazz: NativePtr): NativePtr { private external fun 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") diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt index 7345ec3eaf3..6f4a121c4e4 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/ObjCStubs.kt @@ -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 -> diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt index a2eae34b030..34a21c67432 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/InteropConfiguration.kt @@ -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, val noStringConversion: Set, val exportForwardDeclarations: List, - val disableDesignatedInitializerChecks: Boolean + val disableDesignatedInitializerChecks: Boolean, + val target: KonanTarget ) \ No newline at end of file diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/ToolConfig.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/ToolConfig.kt index fdec43a39c7..5a0a7b7ff4d 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/ToolConfig.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/ToolConfig.kt @@ -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) diff --git a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt index 5dcad0933f8..4d6ffdf9f02 100644 --- a/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt +++ b/Interop/StubGenerator/src/main/kotlin/org/jetbrains/kotlin/native/interop/gen/jvm/main.kt @@ -277,7 +277,8 @@ private fun processCLib(args: Array): Array? { 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) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt index 7512aafb141..b707779b631 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/InteropUtils.kt @@ -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() diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index d9a2fbc60e0..b2f9c43a611 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -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 { - 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, isLU: Boolean): LLVMValueRef { + private fun genGetObjCMessenger(args: List, 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)