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:
committed by
Pavel Punegov
parent
e3fd5d5306
commit
ad8160ae83
+2
-7
@@ -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
|
||||
|
||||
+4
@@ -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(
|
||||
|
||||
+4
@@ -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)!!
|
||||
|
||||
|
||||
+46
-18
@@ -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,
|
||||
|
||||
+37
-16
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+8
-4
@@ -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.
|
||||
|
||||
+8
-11
@@ -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)
|
||||
|
||||
|
||||
|
||||
+6
-3
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user