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 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
@@ -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) =
@@ -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 {
@@ -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
}!! }!!
} }
@@ -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>()
@@ -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)
@@ -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)