Support nullable floating point equality comparisons

This commit is contained in:
Pavel Punegov
2018-02-27 20:38:18 +03:00
committed by Pavel Punegov
parent 095cad4145
commit 0b84718fa2
4 changed files with 38 additions and 27 deletions
@@ -125,6 +125,10 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
symbolTable.referenceSimpleFunction(it) symbolTable.referenceSimpleFunction(it)
} }
val ieee754NullableEquals = context.getInternalFunctions("ieee754NullableEquals").map {
symbolTable.referenceSimpleFunction(it)
}
override val areEqual = symbolTable.referenceSimpleFunction(context.getInternalFunctions("areEqual").single()) override val areEqual = symbolTable.referenceSimpleFunction(context.getInternalFunctions("areEqual").single())
override val ThrowNullPointerException = symbolTable.referenceSimpleFunction( override val ThrowNullPointerException = symbolTable.referenceSimpleFunction(
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.Context
import org.jetbrains.kotlin.backend.konan.isValueType import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor 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.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
@@ -32,6 +33,7 @@ import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.types.KotlinType 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.isNothing
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -72,28 +74,21 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
return expression return expression
} }
private fun isIeee754Equals(descriptor: FunctionDescriptor): Boolean = private fun ieee754EqualsDescriptors(): List<FunctionDescriptor> =
irBuiltins.ieee754equalsFunByOperandType.values.any { it.descriptor == descriptor } irBuiltins.ieee754equalsFunByOperandType.values.map(IrSimpleFunction::descriptor)
private fun transformBuiltinOperator(expression: IrCall): IrExpression { private fun transformBuiltinOperator(expression: IrCall): IrExpression = when (expression.descriptor) {
val descriptor = expression.descriptor irBuiltins.eqeq, in ieee754EqualsDescriptors() -> lowerEqeq(expression)
// IEEE754 comparison for floating point values are done by intrinsic irBuiltins.eqeqeq -> lowerEqeqeq(expression)
if (isIeee754Equals(descriptor)) return lowerEqeq(expression)
return when (descriptor) { irBuiltins.throwNpe -> IrCallImpl(expression.startOffset, expression.endOffset,
irBuiltins.eqeq -> lowerEqeq(expression) 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, else -> expression
context.ir.symbols.ThrowNullPointerException)
irBuiltins.noWhenBranchMatchedException -> IrCallImpl(expression.startOffset, expression.endOffset,
context.ir.symbols.ThrowNoWhenBranchMatchedException)
else -> expression
}
} }
private fun lowerEqeqeq(expression: IrCall): IrExpression { 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. // and thus can be declared synthetically in the compiler instead of explicitly in the runtime.
// Find a type-compatible `konan.internal.ieee754Equals` intrinsic: // Find a type-compatible `konan.internal.ieee754Equals` intrinsic:
if (isIeee754Equals(expression.descriptor)) { if (expression.descriptor in ieee754EqualsDescriptors()) {
// FIXME: intrinsic should be also compatible with nullable types return if (lhs.type.isNullable() || rhs.type.isNullable())
selectIntrinsic(context.ir.symbols.ieee754Equals, lhs.type, rhs.type)?.let { selectIntrinsic(context.ir.symbols.ieee754NullableEquals, lhs.type, rhs.type)!!
return it else
} selectIntrinsic(context.ir.symbols.ieee754Equals, lhs.type, rhs.type)!!
} }
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic: // Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
@@ -158,9 +153,7 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
} }
private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType): private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType):
IrSimpleFunctionSymbol? { IrSimpleFunctionSymbol? = from.atMostOne {
return from.atMostOne { lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type)
lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type)
}
} }
} }
@@ -1,4 +1,4 @@
// IGNORE_BACKEND: JS // IGNORE_BACKEND: JS, NATIVE
fun box(): String { fun box(): String {
val zero: Any = 0.0 val zero: Any = 0.0
val floatZero: Any = -0.0F val floatZero: Any = -0.0F
@@ -30,6 +30,20 @@ import kotlinx.cinterop.NativePtr
@Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean @Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean
@Intrinsic external fun ieee754Equals(first: Double, second: Double): 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: NativePtr, second: NativePtr): Boolean
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean @Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
@Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean @Intrinsic external fun areEqualByValue(first: CPointer<*>?, second: CPointer<*>?): Boolean