backend: support native interop value types
also improve reliability of `==` lowering
This commit is contained in:
committed by
SvyatoslavScherbina
parent
1bafee0ac3
commit
7f90ce3d8a
+2
@@ -27,6 +27,8 @@ internal class InteropBuiltIns(builtIns: KonanBuiltIns) {
|
||||
|
||||
val getPointerSize = packageScope.getContributedFunctions("getPointerSize").single()
|
||||
|
||||
val nullableInteropValueTypes = listOf(ValueType.C_POINTER, ValueType.NATIVE_POINTED)
|
||||
|
||||
private val nativePtr = packageScope.getContributedClassifier(nativePtrName) as ClassDescriptor
|
||||
|
||||
private val nativePointed = packageScope.getContributedClassifier(nativePointedName) as ClassDescriptor
|
||||
|
||||
+5
-1
@@ -24,7 +24,11 @@ enum class ValueType(val classFqName: FqNameUnsafe, val isNullable: Boolean = fa
|
||||
FLOAT(KotlinBuiltIns.FQ_NAMES._float),
|
||||
DOUBLE(KotlinBuiltIns.FQ_NAMES._double),
|
||||
|
||||
UNBOUND_CALLABLE_REFERENCE(FqNameUnsafe("konan.internal.UnboundCallableReference"))
|
||||
UNBOUND_CALLABLE_REFERENCE(FqNameUnsafe("konan.internal.UnboundCallableReference")),
|
||||
NATIVE_PTR(InteropBuiltIns.FqNames.nativePtr),
|
||||
|
||||
NATIVE_POINTED(InteropBuiltIns.FqNames.nativePointed, isNullable = true),
|
||||
C_POINTER(InteropBuiltIns.FqNames.cPointer, isNullable = true)
|
||||
}
|
||||
|
||||
private fun KotlinType.isConstructedFromGivenClass(fqName: FqNameUnsafe) =
|
||||
|
||||
+2
@@ -65,6 +65,8 @@ internal fun IrMemberAccessExpression.addArguments(args: Map<ParameterDescriptor
|
||||
internal fun IrMemberAccessExpression.addArguments(args: List<Pair<ParameterDescriptor, IrExpression>>) =
|
||||
this.addArguments(args.toMap())
|
||||
|
||||
internal fun IrExpression.isNullConst() = this is IrConst<*> && this.kind == IrConstKind.Null
|
||||
|
||||
fun ir2string(ir: IrElement?): String = ir2stringWhole(ir).takeWhile { it != '\n' }
|
||||
|
||||
fun ir2stringWhole(ir: IrElement?): String {
|
||||
|
||||
+3
-1
@@ -15,7 +15,9 @@ private val valueTypes = ValueType.values().associate {
|
||||
ValueType.LONG -> LLVMInt64Type()
|
||||
ValueType.FLOAT -> LLVMFloatType()
|
||||
ValueType.DOUBLE -> LLVMDoubleType()
|
||||
ValueType.UNBOUND_CALLABLE_REFERENCE -> int8TypePtr
|
||||
|
||||
ValueType.UNBOUND_CALLABLE_REFERENCE,
|
||||
ValueType.NATIVE_PTR, ValueType.NATIVE_POINTED, ValueType.C_POINTER -> int8TypePtr
|
||||
}!!
|
||||
}
|
||||
|
||||
|
||||
+14
@@ -6,8 +6,10 @@ import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.ValueType
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isNullConst
|
||||
import org.jetbrains.kotlin.backend.konan.notNullableIsRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
||||
import org.jetbrains.kotlin.backend.konan.util.atMostOne
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -103,6 +105,10 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
|
||||
override fun IrExpression.useAs(type: KotlinType): IrExpression {
|
||||
val interop = context.interopBuiltIns
|
||||
if (this.isNullConst() && interop.nullableInteropValueTypes.any { type.isRepresentedAs(it) }) {
|
||||
return IrCallImpl(startOffset, endOffset, interop.getNativeNullPtr).uncheckedCast(type)
|
||||
}
|
||||
|
||||
val actualType = when (this) {
|
||||
is IrCall -> this.descriptor.original.returnType ?: this.type
|
||||
@@ -176,6 +182,14 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
||||
}
|
||||
|
||||
private fun IrExpression.unbox(valueType: ValueType): IrExpression {
|
||||
val unboxFunctionName = "unbox${valueType.shortName}"
|
||||
|
||||
context.builtIns.getKonanInternalFunctions(unboxFunctionName).atMostOne()?.let {
|
||||
return IrCallImpl(startOffset, endOffset, it).apply {
|
||||
putValueArgument(0, this@unbox.uncheckedCast(it.valueParameters[0].type))
|
||||
}.uncheckedCast(this.type)
|
||||
}
|
||||
|
||||
val boxGetter = getBoxType(valueType)
|
||||
.memberScope.getContributedDescriptors()
|
||||
.filterIsInstance<PropertyDescriptor>()
|
||||
|
||||
+10
-2
@@ -3,6 +3,8 @@ package org.jetbrains.kotlin.backend.konan.lower
|
||||
import org.jetbrains.kotlin.backend.common.BodyLoweringPass
|
||||
import org.jetbrains.kotlin.backend.konan.Context
|
||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
||||
import org.jetbrains.kotlin.backend.konan.ir.isNullConst
|
||||
import org.jetbrains.kotlin.backend.konan.util.atMostOne
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptor
|
||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
@@ -69,9 +71,15 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
|
||||
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
|
||||
|
||||
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
|
||||
val equals = builtIns.getKonanInternalFunctions("areEqualByValue").firstOrNull {
|
||||
val equals = builtIns.getKonanInternalFunctions("areEqualByValue").atMostOne {
|
||||
lhs.type.isSubtypeOf(it.valueParameters[0].type) && rhs.type.isSubtypeOf(it.valueParameters[1].type)
|
||||
} ?: builtIns.getKonanInternalFunctions("areEqual").single() // or use the general implementation.
|
||||
} ?: if (lhs.isNullConst() || rhs.isNullConst()) {
|
||||
// or compare by reference if left or right part is `null`:
|
||||
irBuiltins.eqeqeq
|
||||
} else {
|
||||
// or use the general implementation:
|
||||
builtIns.getKonanInternalFunctions("areEqual").single()
|
||||
}
|
||||
|
||||
return IrCallImpl(startOffset, endOffset, equals).apply {
|
||||
putValueArgument(0, lhs)
|
||||
|
||||
+9
@@ -20,3 +20,12 @@ fun nTabs(amount: Int): String {
|
||||
return String.format("%1$-${(amount+1)*4}s", "")
|
||||
}
|
||||
|
||||
fun <T> Collection<T>.atMostOne(): T? {
|
||||
return when (this.size) {
|
||||
0 -> null
|
||||
1 -> this.iterator().next()
|
||||
else -> throw IllegalArgumentException("Collection has more than one element.")
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T> Iterable<T>.atMostOne(predicate: (T) -> Boolean): T? = this.filter(predicate).atMostOne()
|
||||
@@ -0,0 +1,53 @@
|
||||
package konan.internal
|
||||
|
||||
import kotlinx.cinterop.*
|
||||
|
||||
class NativePtrBox(val value: NativePtr) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is NativePtrBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
}
|
||||
|
||||
fun boxNativePtr(value: NativePtr) = NativePtrBox(value)
|
||||
|
||||
class NativePointedBox(val value: NativePointed) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is NativePointedBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
}
|
||||
|
||||
fun boxNativePointed(value: NativePointed?) = if (value != null) NativePointedBox(value) else null
|
||||
fun unboxNativePointed(box: NativePointedBox?) = box?.value
|
||||
|
||||
class CPointerBox(val value: CPointer<*>) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other !is CPointerBox) {
|
||||
return false
|
||||
}
|
||||
|
||||
return this.value == other.value
|
||||
}
|
||||
|
||||
override fun hashCode() = value.hashCode()
|
||||
|
||||
override fun toString() = value.toString()
|
||||
}
|
||||
|
||||
fun boxCPointer(value: CPointer<*>?) = if (value != null) CPointerBox(value) else null
|
||||
fun unboxCPointer(box: CPointerBox?) = box?.value
|
||||
@@ -1,5 +1,9 @@
|
||||
package konan.internal
|
||||
|
||||
import kotlinx.cinterop.CPointer
|
||||
import kotlinx.cinterop.NativePointed
|
||||
import kotlinx.cinterop.NativePtr
|
||||
|
||||
@Intrinsic external fun areEqualByValue(first: Boolean, second: Boolean): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: Char, second: Char): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: Byte, second: Byte): Boolean
|
||||
@@ -9,9 +13,9 @@ package konan.internal
|
||||
@Intrinsic external fun areEqualByValue(first: Float, second: Float): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: Double, second: Double): Boolean
|
||||
|
||||
// For comparing with `null`:
|
||||
@Intrinsic external fun areEqualByValue(first: Nothing?, second: Any?): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: Any?, second: Nothing?): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
|
||||
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean
|
||||
|
||||
inline fun areEqual(first: Any?, second: Any?): Boolean {
|
||||
return if (first == null) second == null else first.equals(second)
|
||||
|
||||
Reference in New Issue
Block a user