backend: support native interop value types

also improve reliability of `==` lowering
This commit is contained in:
Svyatoslav Scherbina
2017-02-02 17:29:31 +07:00
committed by SvyatoslavScherbina
parent 1bafee0ac3
commit 7f90ce3d8a
9 changed files with 105 additions and 7 deletions
@@ -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
@@ -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) =
@@ -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 {
@@ -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
}!!
}
@@ -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>()
@@ -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)
@@ -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)