From 0b84718fa268a5015da1d5e7a8c2c42d61191d83 Mon Sep 17 00:00:00 2001 From: Pavel Punegov Date: Tue, 27 Feb 2018 20:38:18 +0300 Subject: [PATCH] Support nullable floating point equality comparisons --- .../jetbrains/kotlin/backend/konan/ir/Ir.kt | 4 ++ .../konan/lower/BuiltinOperatorLowering.kt | 45 ++++++++----------- .../box/ieee754/smartCastToDifferentTypes.kt | 2 +- .../main/kotlin/konan/internal/Intrinsics.kt | 14 ++++++ 4 files changed, 38 insertions(+), 27 deletions(-) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt index a009f78626d..971d9bdbadb 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ir/Ir.kt @@ -125,6 +125,10 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym symbolTable.referenceSimpleFunction(it) } + val ieee754NullableEquals = context.getInternalFunctions("ieee754NullableEquals").map { + symbolTable.referenceSimpleFunction(it) + } + override val areEqual = symbolTable.referenceSimpleFunction(context.getInternalFunctions("areEqual").single()) override val ThrowNullPointerException = symbolTable.referenceSimpleFunction( diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt index 294cddc2ef8..4704885a46d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/lower/BuiltinOperatorLowering.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.common.atMostOne import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.isValueType import org.jetbrains.kotlin.descriptors.FunctionDescriptor +import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptor import org.jetbrains.kotlin.ir.expressions.IrBody import org.jetbrains.kotlin.ir.expressions.IrCall @@ -32,6 +33,7 @@ import org.jetbrains.kotlin.ir.util.isNullConst import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.isNullable import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf @@ -72,28 +74,21 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf return expression } - private fun isIeee754Equals(descriptor: FunctionDescriptor): Boolean = - irBuiltins.ieee754equalsFunByOperandType.values.any { it.descriptor == descriptor } + private fun ieee754EqualsDescriptors(): List = + irBuiltins.ieee754equalsFunByOperandType.values.map(IrSimpleFunction::descriptor) - private fun transformBuiltinOperator(expression: IrCall): IrExpression { - val descriptor = expression.descriptor + private fun transformBuiltinOperator(expression: IrCall): IrExpression = when (expression.descriptor) { + irBuiltins.eqeq, in ieee754EqualsDescriptors() -> lowerEqeq(expression) - // IEEE754 comparison for floating point values are done by intrinsic - if (isIeee754Equals(descriptor)) return lowerEqeq(expression) + irBuiltins.eqeqeq -> lowerEqeqeq(expression) - return when (descriptor) { - irBuiltins.eqeq -> lowerEqeq(expression) + irBuiltins.throwNpe -> IrCallImpl(expression.startOffset, expression.endOffset, + context.ir.symbols.ThrowNullPointerException) - irBuiltins.eqeqeq -> lowerEqeqeq(expression) + irBuiltins.noWhenBranchMatchedException -> IrCallImpl(expression.startOffset, expression.endOffset, + context.ir.symbols.ThrowNoWhenBranchMatchedException) - irBuiltins.throwNpe -> IrCallImpl(expression.startOffset, expression.endOffset, - context.ir.symbols.ThrowNullPointerException) - - irBuiltins.noWhenBranchMatchedException -> IrCallImpl(expression.startOffset, expression.endOffset, - context.ir.symbols.ThrowNoWhenBranchMatchedException) - - else -> expression - } + else -> expression } private fun lowerEqeqeq(expression: IrCall): IrExpression { @@ -136,11 +131,11 @@ 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.ieee754Equals` intrinsic: - if (isIeee754Equals(expression.descriptor)) { - // FIXME: intrinsic should be also compatible with nullable types - selectIntrinsic(context.ir.symbols.ieee754Equals, lhs.type, rhs.type)?.let { - return it - } + if (expression.descriptor in ieee754EqualsDescriptors()) { + return if (lhs.type.isNullable() || rhs.type.isNullable()) + selectIntrinsic(context.ir.symbols.ieee754NullableEquals, lhs.type, rhs.type)!! + else + selectIntrinsic(context.ir.symbols.ieee754Equals, lhs.type, rhs.type)!! } // Find a type-compatible `konan.internal.areEqualByValue` intrinsic: @@ -158,9 +153,7 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf } private fun selectIntrinsic(from: List, lhsType: KotlinType, rhsType: KotlinType): - IrSimpleFunctionSymbol? { - return from.atMostOne { - lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type) - } + IrSimpleFunctionSymbol? = from.atMostOne { + lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type) } } diff --git a/backend.native/tests/external/codegen/box/ieee754/smartCastToDifferentTypes.kt b/backend.native/tests/external/codegen/box/ieee754/smartCastToDifferentTypes.kt index e3a06426db3..98b087e66e0 100644 --- a/backend.native/tests/external/codegen/box/ieee754/smartCastToDifferentTypes.kt +++ b/backend.native/tests/external/codegen/box/ieee754/smartCastToDifferentTypes.kt @@ -1,4 +1,4 @@ -// IGNORE_BACKEND: JS +// IGNORE_BACKEND: JS, NATIVE fun box(): String { val zero: Any = 0.0 val floatZero: Any = -0.0F diff --git a/runtime/src/main/kotlin/konan/internal/Intrinsics.kt b/runtime/src/main/kotlin/konan/internal/Intrinsics.kt index 2d3a24af2a7..f79d0cc7ec7 100644 --- a/runtime/src/main/kotlin/konan/internal/Intrinsics.kt +++ b/runtime/src/main/kotlin/konan/internal/Intrinsics.kt @@ -30,6 +30,20 @@ import kotlinx.cinterop.NativePtr @Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean @Intrinsic external fun ieee754Equals(first: Double, second: Double): Boolean +@Suppress("NOTHING_TO_INLINE") +inline fun ieee754NullableEquals(first: Float?, second: Float?): Boolean = when { + first == null -> second == null + second == null -> false + else -> ieee754Equals(first, second) +} + +@Suppress("NOTHING_TO_INLINE") +inline fun ieee754NullableEquals(first: Double?, second: Double?): Boolean = when { + first == null -> second == null + second == null -> false + else -> ieee754Equals(first, second) +} + @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