Support IEEE754 floating point comparisons

Add an intrinsic that performs check according to the standard
Add floating point compare methods from LLVM
Support new IR builtins, symbols and descriptors
Rewrite compareTo floating point checks to Kotlin
Rewrite equals to be the same as in JVM
This commit is contained in:
Pavel Punegov
2018-02-22 15:04:20 +03:00
committed by Pavel Punegov
parent e3fd5d5306
commit ad8160ae83
13 changed files with 184 additions and 130 deletions
@@ -18,8 +18,6 @@ package org.jetbrains.kotlin.backend.konan.descriptors
import org.jetbrains.kotlin.backend.common.atMostOne
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns
import org.jetbrains.kotlin.backend.konan.ValueType
import org.jetbrains.kotlin.backend.konan.isRepresentedAs
import org.jetbrains.kotlin.backend.konan.isValueType
import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
@@ -30,10 +28,7 @@ import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope
@@ -74,10 +69,10 @@ internal fun <T : CallableMemberDescriptor> T.resolveFakeOverride(): T {
private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
internal val FunctionDescriptor.isIntrinsic: Boolean
internal val CallableDescriptor.isIntrinsic: Boolean
get() = when {
this.annotations.hasAnnotation(intrinsicAnnotation) -> {
check(isExternal, { "Intrinsic function $name should be external" })
// check(isExternal, { "Intrinsic function $name should be external" })
true
}
else -> false
@@ -121,6 +121,10 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
symbolTable.referenceSimpleFunction(it)
}
val ieee754Equals = context.getInternalFunctions("ieee754Equals").map {
symbolTable.referenceSimpleFunction(it)
}
override val areEqual = symbolTable.referenceSimpleFunction(context.getInternalFunctions("areEqual").single())
override val ThrowNullPointerException = symbolTable.referenceSimpleFunction(
@@ -458,6 +458,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
/* floating-point comparisons */
fun fcmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOEQ, arg0, arg1, name)!!
fun fcmpGt(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOGT, arg0, arg1, name)!!
fun fcmpGe(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOGE, arg0, arg1, name)!!
fun fcmpLt(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOLT, arg0, arg1, name)!!
fun fcmpLe(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOLE, arg0, arg1, name)!!
fun bitcast(type: LLVMTypeRef?, value: LLVMValueRef, name: String = "") = LLVMBuildBitCast(builder, value, type, name)!!
@@ -44,9 +44,9 @@ import org.jetbrains.kotlin.ir.visitors.acceptVoid
import org.jetbrains.kotlin.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
@@ -2016,15 +2016,21 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
"konan.internal.areEqualByValue" -> {
val arg0 = args[0]
val arg1 = args[1]
assert (arg0.type == arg1.type, { "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
assert (arg0.type == arg1.type,
{ "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
return when (LLVMGetTypeKind(arg0.type)) {
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind ->
functionGenerationContext.fcmpEq(arg0, arg1)
return functionGenerationContext.icmpEq(arg0, arg1)
}
"konan.internal.ieee754Equals" -> {
val arg0 = args[0]
val arg1 = args[1]
assert (arg0.type == arg1.type,
{ "Types are different: '${llvmtype2string(arg0.type)}' and '${llvmtype2string(arg1.type)}'" })
val type = LLVMGetTypeKind(arg0.type)
assert (type == LLVMTypeKind.LLVMFloatTypeKind || type == LLVMTypeKind.LLVMDoubleTypeKind,
{ "Should be of floating point kind, not: '${llvmtype2string(arg0.type)}'"})
return functionGenerationContext.fcmpEq(arg0, arg1)
else ->
functionGenerationContext.icmpEq(arg0, arg1)
}
}
"konan.internal.getContinuation" -> return getContinuation()
}
@@ -2332,22 +2338,44 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
private val kFalse = LLVMConstInt(LLVMInt1Type(), 0, 1)!!
private fun evaluateOperatorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef {
context.log{"evaluateCall : origin:${ir2string(callee)}"}
context.log{"evaluateOperatorCall : origin:${ir2string(callee)}"}
val descriptor = callee.descriptor
val ib = context.irModule!!.irBuiltins
when (descriptor) {
ib.eqeqeq -> return functionGenerationContext.icmpEq(args[0], args[1])
ib.gt0 -> return functionGenerationContext.icmpGt(args[0], kImmZero)
ib.gteq0 -> return functionGenerationContext.icmpGe(args[0], kImmZero)
ib.lt0 -> return functionGenerationContext.icmpLt(args[0], kImmZero)
ib.lteq0 -> return functionGenerationContext.icmpLe(args[0], kImmZero)
ib.booleanNot -> return functionGenerationContext.icmpNe(args[0], kTrue)
else -> {
TODO(descriptor.name.toString())
val funGen = functionGenerationContext
return when {
descriptor == ib.eqeqeq -> funGen.icmpEq(args[0], args[1])
descriptor == ib.booleanNot -> funGen.icmpNe(args[0], kTrue)
descriptor.isComparisonDescriptor(ib.greaterFunByOperandType) -> {
if (args[0].type.isFloatingPoint()) funGen.fcmpGt(args[0], args[1])
else funGen.icmpGt(args[0], args[1])
}
descriptor.isComparisonDescriptor(ib.greaterOrEqualFunByOperandType) -> {
if (args[0].type.isFloatingPoint()) funGen.fcmpGe(args[0], args[1])
else funGen.icmpGe(args[0], args[1])
}
descriptor.isComparisonDescriptor(ib.lessFunByOperandType) -> {
if (args[0].type.isFloatingPoint()) funGen.fcmpLt(args[0], args[1])
else funGen.icmpLt(args[0], args[1])
}
descriptor.isComparisonDescriptor(ib.lessOrEqualFunByOperandType) -> {
if (args[0].type.isFloatingPoint()) funGen.fcmpLe(args[0], args[1])
else funGen.icmpLe(args[0], args[1])
}
else -> TODO(descriptor.name.toString())
}
}
private fun LLVMTypeRef.isFloatingPoint(): Boolean {
val typeKind = LLVMGetTypeKind(this)
return typeKind == LLVMTypeKind.LLVMFloatTypeKind || typeKind == LLVMTypeKind.LLVMDoubleTypeKind
}
private fun FunctionDescriptor.isComparisonDescriptor(map: Map<SimpleType, IrSimpleFunction>): Boolean {
return map.values.any { it.descriptor == this }
}
//-------------------------------------------------------------------------//
private fun generateWhenCase(resultPhi: LLVMValueRef?, branch: IrBranch,
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.backend.common.BodyLoweringPass
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.descriptors.IrBuiltinOperatorDescriptor
import org.jetbrains.kotlin.ir.expressions.IrBody
import org.jetbrains.kotlin.ir.expressions.IrCall
@@ -28,9 +29,9 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.isNullConst
import org.jetbrains.kotlin.ir.util.type
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.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -71,14 +72,17 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
return expression
}
private fun isIeee754Equals(descriptor: FunctionDescriptor): Boolean =
irBuiltins.ieee754equalsFunByOperandType.values.any { it.descriptor == descriptor }
private fun transformBuiltinOperator(expression: IrCall): IrExpression {
val descriptor = expression.descriptor
// IEEE754 comparison for floating point values are done by intrinsic
if (isIeee754Equals(descriptor)) return lowerEqeq(expression)
return when (descriptor) {
irBuiltins.eqeq -> {
lowerEqeq(expression.getValueArgument(0)!!, expression.getValueArgument(1)!!,
expression.startOffset, expression.endOffset)
}
irBuiltins.eqeq -> lowerEqeq(expression)
irBuiltins.eqeqeq -> lowerEqeqeq(expression)
@@ -98,39 +102,49 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
return if (lhs.type.isValueType() && rhs.type.isValueType()) {
// Achieve the same behavior as with JVM BE: if both sides of `===` are values, then compare by value:
lowerEqeq(lhs, rhs, expression.startOffset, expression.endOffset)
lowerEqeq(expression)
// Note: such comparisons are deprecated.
} else {
expression
}
}
private fun lowerEqeq(lhs: IrExpression, rhs: IrExpression, startOffset: Int, endOffset: Int): IrExpression {
private fun lowerEqeq(expression: IrCall): IrExpression {
// TODO: optimize boxing?
val startOffset = expression.startOffset
val endOffset = expression.endOffset
val equals = selectEqualsFunction(lhs, rhs)
val equals = selectEqualsFunction(expression)
return IrCallImpl(startOffset, endOffset, equals).apply {
putValueArgument(0, lhs)
putValueArgument(1, rhs)
putValueArgument(0, expression.getValueArgument(0)!!)
putValueArgument(1, expression.getValueArgument(1)!!)
}
}
private fun selectEqualsFunction(lhs: IrExpression, rhs: IrExpression): IrSimpleFunctionSymbol {
private fun selectEqualsFunction(expression: IrCall): IrSimpleFunctionSymbol {
val lhs = expression.getValueArgument(0)!!
val rhs = expression.getValueArgument(1)!!
val nullableNothingType = builtIns.nullableNothingType
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
// Compare by reference if each part is either `Nothing` or `Nothing?`:
return irBuiltins.eqeqeqSymbol
}
// TODO: areEqualByValue intrinsics are specially treated by code generator
// TODO: areEqualByValue and ieee754Equals intrinsics are specially treated by code generator
// 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
}
}
// Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
context.ir.symbols.areEqualByValue.atMostOne {
lhs.type.isSubtypeOf(it.owner.valueParameters[0].type) &&
rhs.type.isSubtypeOf(it.owner.valueParameters[1].type)
}?.let {
selectIntrinsic(context.ir.symbols.areEqualByValue, lhs.type, rhs.type)?.let {
return it
}
@@ -142,4 +156,11 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
context.ir.symbols.areEqual
}
}
private fun selectIntrinsic(from: List<IrSimpleFunctionSymbol>, lhsType: KotlinType, rhsType: KotlinType):
IrSimpleFunctionSymbol? {
return from.atMostOne {
lhsType.isSubtypeOf(it.owner.valueParameters[0].type) && rhsType.isSubtypeOf(it.owner.valueParameters[1].type)
}
}
}
@@ -424,10 +424,10 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
// Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
// Check if forLoopInfo.bound > MIN_VALUE.
val progressionType = forLoopInfo.progressionInfo.progressionType
return irCall(context.irBuiltIns.gt0Symbol).apply {
return irCall(context.irBuiltIns.greaterFunByOperandType[context.irBuiltIns.int]?.symbol!!).apply {
val minConst = when {
progressionType.isIntProgression() -> IrConstImpl
.int(startOffset,endOffset, context.builtIns.intType, Int.MIN_VALUE)
.int(startOffset, endOffset, context.builtIns.intType, Int.MIN_VALUE)
progressionType.isCharProgression() -> IrConstImpl
.char(startOffset, endOffset, context.builtIns.charType, 0.toChar())
progressionType.isLongProgression() -> IrConstImpl
@@ -441,21 +441,25 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
putValueArgument(0, minConst)
}
putValueArgument(0, compareToCall)
putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, 0))
}
}
// TODO: Eliminate the loop if we can prove that it will not be executed.
private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
val builtIns = context.irBuiltIns
val increasing = forLoopInfo.progressionInfo.increasing
val comparingBuiltIn = if (increasing) context.irBuiltIns.lteq0Symbol else context.irBuiltIns.gteq0Symbol
val comparingBuiltIn = if (increasing) builtIns.lessOrEqualFunByOperandType[builtIns.int]?.symbol
else builtIns.greaterOrEqualFunByOperandType[builtIns.int]?.symbol
// Check if inductionVariable <= last.
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
forLoopInfo.inductionVariable.descriptor.type,
forLoopInfo.last.descriptor.type)
val check: IrExpression = irCall(comparingBuiltIn).apply {
val check: IrExpression = irCall(comparingBuiltIn!!).apply {
putValueArgument(0, irCallOp(compareTo, irGet(forLoopInfo.inductionVariable), irGet(forLoopInfo.last)))
putValueArgument(1, IrConstImpl.int(startOffset, endOffset, context.builtIns.intType, 0))
}
// Process closed and open ranges in different manners.
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.backend.konan.llvm.isExported
import org.jetbrains.kotlin.backend.konan.llvm.typeInfoSymbolName
import org.jetbrains.kotlin.backend.konan.llvm.symbolName
import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
internal fun DeclarationDescriptor.symbolName(): String = when (this) {
is FunctionDescriptor
@@ -81,16 +82,12 @@ class IrDeserializationDescriptorIndex(irBuiltIns: IrBuiltIns) {
}
val IrBuiltIns.irBuiltInDescriptors
get() = listOf<FunctionDescriptor>(
this.eqeqeq,
this.eqeq,
this.lt0,
this.lteq0,
this.gt0,
this.gteq0,
this.throwNpe,
this.booleanNot,
this.noWhenBranchMatchedException,
this.enumValueOf)
get() = (lessFunByOperandType.values +
lessOrEqualFunByOperandType.values +
greaterOrEqualFunByOperandType.values +
greaterFunByOperandType.values +
ieee754equalsFunByOperandType.values +
eqeqeqFun + eqeqFun + throwNpeFun + booleanNotFun + noWhenBranchMatchedExceptionFun + enumValueOfFun)
.map(IrSimpleFunction::descriptor)
@@ -40,9 +40,12 @@ internal fun IrModuleFragment.replaceUnboundSymbols(context: Context) {
val collector = DeclarationSymbolCollector()
with(collector) {
with(irBuiltins) {
for (op in arrayOf(eqeqeqFun, eqeqFun, lt0Fun, lteq0Fun, gt0Fun, gteq0Fun, throwNpeFun, booleanNotFun,
noWhenBranchMatchedExceptionFun)) {
for (op in arrayOf(eqeqeqFun, eqeqFun, throwNpeFun, booleanNotFun, noWhenBranchMatchedExceptionFun) +
lessFunByOperandType.values +
lessOrEqualFunByOperandType.values +
greaterOrEqualFunByOperandType.values +
greaterFunByOperandType.values +
ieee754equalsFunByOperandType.values) {
register(op.symbol)
}
}
+1 -16
View File
@@ -324,13 +324,6 @@ KDouble Kotlin_Long_toDouble (KLong a ) { return a; }
//--- Float -------------------------------------------------------------------//
KInt Kotlin_Float_compareTo_Byte (KFloat a, KByte b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Float_compareTo_Short (KFloat a, KShort b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Float_compareTo_Int (KFloat a, KInt b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Float_compareTo_Long (KFloat a, KLong b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Float_compareTo_Float (KFloat a, KFloat b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Float_compareTo_Double (KFloat a, KDouble b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KFloat Kotlin_Float_plus_Byte (KFloat a, KByte b) { return a + b; }
KFloat Kotlin_Float_plus_Short (KFloat a, KShort b) { return a + b; }
KFloat Kotlin_Float_plus_Int (KFloat a, KInt b) { return a + b; }
@@ -412,13 +405,6 @@ KBoolean Kotlin_Float_isFinite (KFloat a) { return isfinite(a);
//--- Double ------------------------------------------------------------------//
KInt Kotlin_Double_compareTo_Byte (KDouble a, KByte b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Double_compareTo_Short (KDouble a, KShort b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Double_compareTo_Int (KDouble a, KInt b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Double_compareTo_Long (KDouble a, KLong b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Double_compareTo_Float (KDouble a, KFloat b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KInt Kotlin_Double_compareTo_Double (KDouble a, KDouble b) { if (a == b) return 0; return (a < b) ? -1 : 1; }
KDouble Kotlin_Double_plus_Byte (KDouble a, KByte b) { return a + b; }
KDouble Kotlin_Double_plus_Short (KDouble a, KShort b) { return a + b; }
KDouble Kotlin_Double_plus_Int (KDouble a, KInt b) { return a + b; }
@@ -471,8 +457,7 @@ KLong Kotlin_Double_toLong (KDouble a ) {
if (a <= (KDouble) INT64_MIN) return INT64_MIN;
return a;
}
KByte Kotlin_Double_toByte (KDouble a ) { return (KByte) Kotlin_Double_toInt(a); }
KShort Kotlin_Double_toShort (KDouble a ) { return (KShort) Kotlin_Double_toInt(a); }
KFloat Kotlin_Double_toFloat (KDouble a ) { return a; }
KDouble Kotlin_Double_toDouble (KDouble a ) { return a; }
@@ -170,7 +170,7 @@ class FloatBox(val value: Float) : Number(), Comparable<Float> {
return false
}
return this.value == other.value
return this.value.equals(other.value)
}
override fun hashCode() = value.hashCode()
@@ -197,7 +197,7 @@ class DoubleBox(val value: Double) : Number(), Comparable<Double> {
return false
}
return this.value == other.value
return this.value.equals(other.value)
}
override fun hashCode() = value.hashCode()
@@ -26,8 +26,9 @@ import kotlinx.cinterop.NativePtr
@Intrinsic external fun areEqualByValue(first: Short, second: Short): Boolean
@Intrinsic external fun areEqualByValue(first: Int, second: Int): Boolean
@Intrinsic external fun areEqualByValue(first: Long, second: Long): Boolean
@Intrinsic external fun areEqualByValue(first: Float, second: Float): Boolean
@Intrinsic external fun areEqualByValue(first: Double, second: Double): Boolean
@Intrinsic external fun ieee754Equals(first: Float, second: Float): Boolean
@Intrinsic external fun ieee754Equals(first: Double, second: Double): Boolean
@Intrinsic external fun areEqualByValue(first: NativePtr, second: NativePtr): Boolean
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): Boolean
+62 -50
View File
@@ -983,43 +983,52 @@ public final class Float : Number(), Comparable<Float> {
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Byte")
external public operator fun compareTo(other: Byte): Int
public operator fun compareTo(other: Byte): Int = compareTo(other.toFloat())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Short")
external public operator fun compareTo(other: Short): Int
public operator fun compareTo(other: Short): Int = compareTo(other.toFloat())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Int")
external public operator fun compareTo(other: Int): Int
public operator fun compareTo(other: Int): Int = compareTo(other.toFloat())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Long")
external public operator fun compareTo(other: Long): Int
public operator fun compareTo(other: Long): Int = compareTo(other.toFloat())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Float")
external public override operator fun compareTo(other: Float): Int
public override operator fun compareTo(other: Float): Int {
// if any of values in NaN both comparisons return false
if (this > other) return 1
if (this < other) return -1
val thisBits = this.toBits()
val otherBits = other.toBits()
// Canonical NaN bits representation higher than any other bit representvalue
return when {
(thisBits > otherBits) -> 1
(thisBits < otherBits) -> -1
else -> 0
}
}
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Float_compareTo_Double")
external public operator fun compareTo(other: Double): Int
public operator fun compareTo(other: Double): Int = - other.toDouble().compareTo(this)
/** Adds the other value to this value. */
@SymbolName("Kotlin_Float_plus_Byte")
@@ -1129,11 +1138,12 @@ public final class Float : Number(), Comparable<Float> {
@SymbolName("Kotlin_Float_unaryMinus")
external public operator fun unaryMinus(): Float
@SymbolName("Kotlin_Float_toByte")
external public override fun toByte(): Byte
public override fun toByte(): Byte = this.toInt().toByte()
public override fun toChar(): Char = this.toInt().toChar()
@SymbolName("Kotlin_Float_toShort")
external public override fun toShort(): Short
public override fun toShort(): Short = this.toInt().toShort()
@SymbolName("Kotlin_Float_toInt")
external public override fun toInt(): Int
@SymbolName("Kotlin_Float_toLong")
@@ -1143,14 +1153,9 @@ public final class Float : Number(), Comparable<Float> {
@SymbolName("Kotlin_Float_toDouble")
external public override fun toDouble(): Double
// Konan-specific.
// We intentionally provide this overload to equals() to avoid artifical boxing.
// Note that here we intentionally deviate from JVM Kotlin, where this method would be
// this.bits() == other.bits().
public fun equals(other: Float): Boolean = konan.internal.areEqualByValue(this, other)
public fun equals(other: Float): Boolean = toBits() == other.toBits()
public override fun equals(other: Any?): Boolean =
other is Float && konan.internal.areEqualByValue(this, other)
public override fun equals(other: Any?): Boolean = other is Float && this.equals(other)
public override fun toString() = NumberConverter.convert(this)
@@ -1201,43 +1206,56 @@ public final class Double : Number(), Comparable<Double> {
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Byte")
external public operator fun compareTo(other: Byte): Int
public operator fun compareTo(other: Byte): Int = compareTo(other.toDouble())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Short")
external public operator fun compareTo(other: Short): Int
public operator fun compareTo(other: Short): Int = compareTo(other.toDouble())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Int")
external public operator fun compareTo(other: Int): Int
public operator fun compareTo(other: Int): Int = compareTo(other.toDouble())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Long")
external public operator fun compareTo(other: Long): Int
public operator fun compareTo(other: Long): Int = compareTo(other.toDouble())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Float")
external public operator fun compareTo(other: Float): Int
public operator fun compareTo(other: Float): Int = compareTo(other.toDouble())
/**
* Compares this value with the specified value for order.
* Returns zero if this value is equal to the specified other value, a negative number if its less than other,
* or a positive number if its greater than other.
*/
@SymbolName("Kotlin_Double_compareTo_Double")
external public override operator fun compareTo(other: Double): Int
public override operator fun compareTo(other: Double): Int {
// if any of values in NaN both comparisons return false
if (this > other) return 1
if (this < other) return -1
val thisBits = this.toBits()
val otherBits = other.toBits()
// Canonical NaN bits representation higher than any other bit representvalue
return when {
(thisBits > otherBits) -> 1
(thisBits < otherBits) -> -1
else -> 0
}
}
/** Adds the other value to this value. */
@SymbolName("Kotlin_Double_plus_Byte")
@@ -1347,14 +1365,14 @@ public final class Double : Number(), Comparable<Double> {
@SymbolName("Kotlin_Double_unaryMinus")
external public operator fun unaryMinus(): Double
public override fun toByte(): Byte = this.toInt().toByte()
@SymbolName("Kotlin_Double_toByte")
external public override fun toByte(): Byte
public override fun toChar(): Char = this.toInt().toChar()
@SymbolName("Kotlin_Double_toShort")
external public override fun toShort(): Short
@SymbolName("Kotlin_Double_toInt")
external public override fun toInt(): Int
public override fun toShort(): Short = this.toInt().toShort()
public override fun toInt(): Int = this.toLong().toInt()
@SymbolName("Kotlin_Double_toLong")
external public override fun toLong(): Long
@SymbolName("Kotlin_Double_toFloat")
@@ -1362,19 +1380,13 @@ public final class Double : Number(), Comparable<Double> {
@SymbolName("Kotlin_Double_toDouble")
external public override fun toDouble(): Double
// Konan-specific.
// Note that here we intentionally deviate from JVM Kotlin, where this method would be
// this.bits() == other.bits().
public fun equals(other: Double): Boolean = konan.internal.areEqualByValue(this, other)
public fun equals(other: Double): Boolean = toBits() == other.toBits()
public override fun equals(other: Any?): Boolean =
other is Double && konan.internal.areEqualByValue(this, other)
public override fun equals(other: Any?): Boolean = other is Double && this.equals(other)
public override fun toString() = NumberConverter.convert(this)
public override fun hashCode(): Int {
return bits().hashCode()
}
public override fun hashCode(): Int = bits().hashCode()
@SymbolName("Kotlin_Double_bits")
external public fun bits(): Long
@@ -266,7 +266,7 @@ public inline fun <T: Comparable<T>> nullsFirst(): Comparator<T?> = nullsFirst(n
* considering `null` value greater than any other value.
*/
public fun <T: Any> nullsLast(comparator: Comparator<in T>): Comparator<T?> {
return object: Comparator<T?> {
return object: Comparator<T?> {
override fun compare(a: T?, b: T?): Int {
if (a === b) return 0
if (a == null) return 1