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 getPointerSize = packageScope.getContributedFunctions("getPointerSize").single()
|
||||||
|
|
||||||
|
val nullableInteropValueTypes = listOf(ValueType.C_POINTER, ValueType.NATIVE_POINTED)
|
||||||
|
|
||||||
private val nativePtr = packageScope.getContributedClassifier(nativePtrName) as ClassDescriptor
|
private val nativePtr = packageScope.getContributedClassifier(nativePtrName) as ClassDescriptor
|
||||||
|
|
||||||
private val nativePointed = packageScope.getContributedClassifier(nativePointedName) 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),
|
FLOAT(KotlinBuiltIns.FQ_NAMES._float),
|
||||||
DOUBLE(KotlinBuiltIns.FQ_NAMES._double),
|
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) =
|
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>>) =
|
internal fun IrMemberAccessExpression.addArguments(args: List<Pair<ParameterDescriptor, IrExpression>>) =
|
||||||
this.addArguments(args.toMap())
|
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 ir2string(ir: IrElement?): String = ir2stringWhole(ir).takeWhile { it != '\n' }
|
||||||
|
|
||||||
fun ir2stringWhole(ir: IrElement?): String {
|
fun ir2stringWhole(ir: IrElement?): String {
|
||||||
|
|||||||
+3
-1
@@ -15,7 +15,9 @@ private val valueTypes = ValueType.values().associate {
|
|||||||
ValueType.LONG -> LLVMInt64Type()
|
ValueType.LONG -> LLVMInt64Type()
|
||||||
ValueType.FLOAT -> LLVMFloatType()
|
ValueType.FLOAT -> LLVMFloatType()
|
||||||
ValueType.DOUBLE -> LLVMDoubleType()
|
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.ValueType
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
|
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalClass
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
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.notNullableIsRepresentedAs
|
||||||
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
|
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.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
@@ -103,6 +105,10 @@ private class AutoboxingTransformer(val context: Context) : AbstractValueUsageTr
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun IrExpression.useAs(type: KotlinType): IrExpression {
|
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) {
|
val actualType = when (this) {
|
||||||
is IrCall -> this.descriptor.original.returnType ?: this.type
|
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 {
|
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)
|
val boxGetter = getBoxType(valueType)
|
||||||
.memberScope.getContributedDescriptors()
|
.memberScope.getContributedDescriptors()
|
||||||
.filterIsInstance<PropertyDescriptor>()
|
.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.common.BodyLoweringPass
|
||||||
import org.jetbrains.kotlin.backend.konan.Context
|
import org.jetbrains.kotlin.backend.konan.Context
|
||||||
import org.jetbrains.kotlin.backend.konan.descriptors.getKonanInternalFunctions
|
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.descriptors.IrBuiltinOperatorDescriptor
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrBody
|
import org.jetbrains.kotlin.ir.expressions.IrBody
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
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.
|
// and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
|
||||||
|
|
||||||
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
|
// 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)
|
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 {
|
return IrCallImpl(startOffset, endOffset, equals).apply {
|
||||||
putValueArgument(0, lhs)
|
putValueArgument(0, lhs)
|
||||||
|
|||||||
+9
@@ -20,3 +20,12 @@ fun nTabs(amount: Int): String {
|
|||||||
return String.format("%1$-${(amount+1)*4}s", "")
|
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
|
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: Boolean, second: Boolean): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: Char, second: Char): Boolean
|
@Intrinsic external fun areEqualByValue(first: Char, second: Char): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: Byte, second: Byte): 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: Float, second: Float): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: Double, second: Double): Boolean
|
@Intrinsic external fun areEqualByValue(first: Double, second: Double): Boolean
|
||||||
|
|
||||||
// For comparing with `null`:
|
@Intrinsic external fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: Nothing?, second: Any?): Boolean
|
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
|
||||||
@Intrinsic external fun areEqualByValue(first: Any?, second: Nothing?): Boolean
|
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean
|
||||||
|
|
||||||
inline fun areEqual(first: Any?, second: Any?): Boolean {
|
inline fun areEqual(first: Any?, second: Any?): Boolean {
|
||||||
return if (first == null) second == null else first.equals(second)
|
return if (first == null) second == null else first.equals(second)
|
||||||
|
|||||||
Reference in New Issue
Block a user