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.common.atMostOne
import org.jetbrains.kotlin.backend.konan.KonanBuiltIns 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.isValueType
import org.jetbrains.kotlin.backend.konan.llvm.functionName import org.jetbrains.kotlin.backend.konan.llvm.functionName
import org.jetbrains.kotlin.backend.konan.llvm.localHash 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.builtins.isSuspendFunctionType
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor 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.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.OverridingUtil import org.jetbrains.kotlin.resolve.OverridingUtil
import org.jetbrains.kotlin.resolve.descriptorUtil.* import org.jetbrains.kotlin.resolve.descriptorUtil.*
import org.jetbrains.kotlin.resolve.scopes.MemberScope 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") private val intrinsicAnnotation = FqName("konan.internal.Intrinsic")
internal val FunctionDescriptor.isIntrinsic: Boolean internal val CallableDescriptor.isIntrinsic: Boolean
get() = when { get() = when {
this.annotations.hasAnnotation(intrinsicAnnotation) -> { this.annotations.hasAnnotation(intrinsicAnnotation) -> {
check(isExternal, { "Intrinsic function $name should be external" }) // check(isExternal, { "Intrinsic function $name should be external" })
true true
} }
else -> false else -> false
@@ -121,6 +121,10 @@ internal class KonanSymbols(context: Context, val symbolTable: SymbolTable): Sym
symbolTable.referenceSimpleFunction(it) symbolTable.referenceSimpleFunction(it)
} }
val ieee754Equals = context.getInternalFunctions("ieee754Equals").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(
@@ -458,6 +458,10 @@ internal class FunctionGenerationContext(val function: LLVMValueRef,
/* floating-point comparisons */ /* floating-point comparisons */
fun fcmpEq(arg0: LLVMValueRef, arg1: LLVMValueRef, name: String = ""): LLVMValueRef = LLVMBuildFCmp(builder, LLVMRealPredicate.LLVMRealOEQ, arg0, arg1, name)!! 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)!! 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.konan.target.KonanTarget
import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.SimpleType
import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.TypeUtils
import org.jetbrains.kotlin.types.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
@@ -2016,15 +2016,21 @@ internal class CodeGeneratorVisitor(val context: Context, val lifetimes: Map<IrE
"konan.internal.areEqualByValue" -> { "konan.internal.areEqualByValue" -> {
val arg0 = args[0] val arg0 = args[0]
val arg1 = args[1] 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)) { return functionGenerationContext.icmpEq(arg0, arg1)
LLVMTypeKind.LLVMFloatTypeKind, LLVMTypeKind.LLVMDoubleTypeKind -> }
functionGenerationContext.fcmpEq(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() "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 val kFalse = LLVMConstInt(LLVMInt1Type(), 0, 1)!!
private fun evaluateOperatorCall(callee: IrCall, args: List<LLVMValueRef>): LLVMValueRef { 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 descriptor = callee.descriptor
val ib = context.irModule!!.irBuiltins val ib = context.irModule!!.irBuiltins
when (descriptor) { val funGen = functionGenerationContext
ib.eqeqeq -> return functionGenerationContext.icmpEq(args[0], args[1])
ib.gt0 -> return functionGenerationContext.icmpGt(args[0], kImmZero) return when {
ib.gteq0 -> return functionGenerationContext.icmpGe(args[0], kImmZero) descriptor == ib.eqeqeq -> funGen.icmpEq(args[0], args[1])
ib.lt0 -> return functionGenerationContext.icmpLt(args[0], kImmZero) descriptor == ib.booleanNot -> funGen.icmpNe(args[0], kTrue)
ib.lteq0 -> return functionGenerationContext.icmpLe(args[0], kImmZero)
ib.booleanNot -> return functionGenerationContext.icmpNe(args[0], kTrue) descriptor.isComparisonDescriptor(ib.greaterFunByOperandType) -> {
else -> { if (args[0].type.isFloatingPoint()) funGen.fcmpGt(args[0], args[1])
TODO(descriptor.name.toString()) 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, 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.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.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
@@ -28,9 +29,9 @@ import org.jetbrains.kotlin.ir.expressions.IrTypeOperatorCall
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
import org.jetbrains.kotlin.ir.util.isNullConst 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.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.typeUtil.isNothing import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
@@ -71,14 +72,17 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
return expression return expression
} }
private fun isIeee754Equals(descriptor: FunctionDescriptor): Boolean =
irBuiltins.ieee754equalsFunByOperandType.values.any { it.descriptor == descriptor }
private fun transformBuiltinOperator(expression: IrCall): IrExpression { private fun transformBuiltinOperator(expression: IrCall): IrExpression {
val descriptor = expression.descriptor val descriptor = expression.descriptor
// IEEE754 comparison for floating point values are done by intrinsic
if (isIeee754Equals(descriptor)) return lowerEqeq(expression)
return when (descriptor) { return when (descriptor) {
irBuiltins.eqeq -> { irBuiltins.eqeq -> lowerEqeq(expression)
lowerEqeq(expression.getValueArgument(0)!!, expression.getValueArgument(1)!!,
expression.startOffset, expression.endOffset)
}
irBuiltins.eqeqeq -> lowerEqeqeq(expression) irBuiltins.eqeqeq -> lowerEqeqeq(expression)
@@ -98,39 +102,49 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
return if (lhs.type.isValueType() && rhs.type.isValueType()) { 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: // 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. // Note: such comparisons are deprecated.
} else { } else {
expression expression
} }
} }
private fun lowerEqeq(lhs: IrExpression, rhs: IrExpression, startOffset: Int, endOffset: Int): IrExpression { private fun lowerEqeq(expression: IrCall): IrExpression {
// TODO: optimize boxing? // 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 { return IrCallImpl(startOffset, endOffset, equals).apply {
putValueArgument(0, lhs) putValueArgument(0, expression.getValueArgument(0)!!)
putValueArgument(1, rhs) 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 val nullableNothingType = builtIns.nullableNothingType
if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) { if (lhs.type.isSubtypeOf(nullableNothingType) && rhs.type.isSubtypeOf(nullableNothingType)) {
// Compare by reference if each part is either `Nothing` or `Nothing?`: // Compare by reference if each part is either `Nothing` or `Nothing?`:
return irBuiltins.eqeqeqSymbol 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. // 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: // Find a type-compatible `konan.internal.areEqualByValue` intrinsic:
context.ir.symbols.areEqualByValue.atMostOne { selectIntrinsic(context.ir.symbols.areEqualByValue, lhs.type, rhs.type)?.let {
lhs.type.isSubtypeOf(it.owner.valueParameters[0].type) &&
rhs.type.isSubtypeOf(it.owner.valueParameters[1].type)
}?.let {
return it return it
} }
@@ -142,4 +156,11 @@ private class BuiltinOperatorTransformer(val context: Context) : IrElementTransf
context.ir.symbols.areEqual 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) {}. // Condition for a corner case: for (i in a until Int.MIN_VALUE) {}.
// Check if forLoopInfo.bound > MIN_VALUE. // Check if forLoopInfo.bound > MIN_VALUE.
val progressionType = forLoopInfo.progressionInfo.progressionType val progressionType = forLoopInfo.progressionInfo.progressionType
return irCall(context.irBuiltIns.gt0Symbol).apply { return irCall(context.irBuiltIns.greaterFunByOperandType[context.irBuiltIns.int]?.symbol!!).apply {
val minConst = when { val minConst = when {
progressionType.isIntProgression() -> IrConstImpl progressionType.isIntProgression() -> IrConstImpl
.int(startOffset,endOffset, context.builtIns.intType, Int.MIN_VALUE) .int(startOffset, endOffset, context.builtIns.intType, Int.MIN_VALUE)
progressionType.isCharProgression() -> IrConstImpl progressionType.isCharProgression() -> IrConstImpl
.char(startOffset, endOffset, context.builtIns.charType, 0.toChar()) .char(startOffset, endOffset, context.builtIns.charType, 0.toChar())
progressionType.isLongProgression() -> IrConstImpl progressionType.isLongProgression() -> IrConstImpl
@@ -441,21 +441,25 @@ private class ForLoopsTransformer(val context: Context) : IrElementTransformerVo
putValueArgument(0, minConst) putValueArgument(0, minConst)
} }
putValueArgument(0, compareToCall) 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. // TODO: Eliminate the loop if we can prove that it will not be executed.
private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression { private fun DeclarationIrBuilder.buildEmptyCheck(loop: IrLoop, forLoopInfo: ForLoopInfo): IrExpression {
val builtIns = context.irBuiltIns
val increasing = forLoopInfo.progressionInfo.increasing 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. // Check if inductionVariable <= last.
val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO, val compareTo = symbols.getBinaryOperator(OperatorNameConventions.COMPARE_TO,
forLoopInfo.inductionVariable.descriptor.type, forLoopInfo.inductionVariable.descriptor.type,
forLoopInfo.last.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(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. // 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.typeInfoSymbolName
import org.jetbrains.kotlin.backend.konan.llvm.symbolName import org.jetbrains.kotlin.backend.konan.llvm.symbolName
import org.jetbrains.kotlin.backend.konan.llvm.localHash import org.jetbrains.kotlin.backend.konan.llvm.localHash
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
internal fun DeclarationDescriptor.symbolName(): String = when (this) { internal fun DeclarationDescriptor.symbolName(): String = when (this) {
is FunctionDescriptor is FunctionDescriptor
@@ -81,16 +82,12 @@ class IrDeserializationDescriptorIndex(irBuiltIns: IrBuiltIns) {
} }
val IrBuiltIns.irBuiltInDescriptors val IrBuiltIns.irBuiltInDescriptors
get() = listOf<FunctionDescriptor>( get() = (lessFunByOperandType.values +
this.eqeqeq, lessOrEqualFunByOperandType.values +
this.eqeq, greaterOrEqualFunByOperandType.values +
this.lt0, greaterFunByOperandType.values +
this.lteq0, ieee754equalsFunByOperandType.values +
this.gt0, eqeqeqFun + eqeqFun + throwNpeFun + booleanNotFun + noWhenBranchMatchedExceptionFun + enumValueOfFun)
this.gteq0, .map(IrSimpleFunction::descriptor)
this.throwNpe,
this.booleanNot,
this.noWhenBranchMatchedException,
this.enumValueOf)
@@ -40,9 +40,12 @@ internal fun IrModuleFragment.replaceUnboundSymbols(context: Context) {
val collector = DeclarationSymbolCollector() val collector = DeclarationSymbolCollector()
with(collector) { with(collector) {
with(irBuiltins) { with(irBuiltins) {
for (op in arrayOf(eqeqeqFun, eqeqFun, lt0Fun, lteq0Fun, gt0Fun, gteq0Fun, throwNpeFun, booleanNotFun, for (op in arrayOf(eqeqeqFun, eqeqFun, throwNpeFun, booleanNotFun, noWhenBranchMatchedExceptionFun) +
noWhenBranchMatchedExceptionFun)) { lessFunByOperandType.values +
lessOrEqualFunByOperandType.values +
greaterOrEqualFunByOperandType.values +
greaterFunByOperandType.values +
ieee754equalsFunByOperandType.values) {
register(op.symbol) register(op.symbol)
} }
} }
+1 -16
View File
@@ -324,13 +324,6 @@ KDouble Kotlin_Long_toDouble (KLong a ) { return a; }
//--- Float -------------------------------------------------------------------// //--- 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_Byte (KFloat a, KByte b) { return a + b; }
KFloat Kotlin_Float_plus_Short (KFloat a, KShort 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; } 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 ------------------------------------------------------------------// //--- 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_Byte (KDouble a, KByte b) { return a + b; }
KDouble Kotlin_Double_plus_Short (KDouble a, KShort 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; } 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; if (a <= (KDouble) INT64_MIN) return INT64_MIN;
return a; 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; } KFloat Kotlin_Double_toFloat (KDouble a ) { return a; }
KDouble Kotlin_Double_toDouble (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 false
} }
return this.value == other.value return this.value.equals(other.value)
} }
override fun hashCode() = value.hashCode() override fun hashCode() = value.hashCode()
@@ -197,7 +197,7 @@ class DoubleBox(val value: Double) : Number(), Comparable<Double> {
return false return false
} }
return this.value == other.value return this.value.equals(other.value)
} }
override fun hashCode() = value.hashCode() 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: Short, second: Short): Boolean
@Intrinsic external fun areEqualByValue(first: Int, second: Int): Boolean @Intrinsic external fun areEqualByValue(first: Int, second: Int): Boolean
@Intrinsic external fun areEqualByValue(first: Long, second: Long): 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: NativePtr, second: NativePtr): Boolean
@Intrinsic external fun areEqualByValue(first: NativePointed?, second: NativePointed?): 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Byte") public operator fun compareTo(other: Byte): Int = compareTo(other.toFloat())
external public operator fun compareTo(other: Byte): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Short") public operator fun compareTo(other: Short): Int = compareTo(other.toFloat())
external public operator fun compareTo(other: Short): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Int") public operator fun compareTo(other: Int): Int = compareTo(other.toFloat())
external public operator fun compareTo(other: Int): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Long") public operator fun compareTo(other: Long): Int = compareTo(other.toFloat())
external public operator fun compareTo(other: Long): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Float") public override operator fun compareTo(other: Float): Int {
external 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. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Float_compareTo_Double") public operator fun compareTo(other: Double): Int = - other.toDouble().compareTo(this)
external public operator fun compareTo(other: Double): Int
/** Adds the other value to this value. */ /** Adds the other value to this value. */
@SymbolName("Kotlin_Float_plus_Byte") @SymbolName("Kotlin_Float_plus_Byte")
@@ -1129,11 +1138,12 @@ public final class Float : Number(), Comparable<Float> {
@SymbolName("Kotlin_Float_unaryMinus") @SymbolName("Kotlin_Float_unaryMinus")
external public operator fun unaryMinus(): Float external public operator fun unaryMinus(): Float
@SymbolName("Kotlin_Float_toByte") public override fun toByte(): Byte = this.toInt().toByte()
external public override fun toByte(): Byte
public override fun toChar(): Char = this.toInt().toChar() 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") @SymbolName("Kotlin_Float_toInt")
external public override fun toInt(): Int external public override fun toInt(): Int
@SymbolName("Kotlin_Float_toLong") @SymbolName("Kotlin_Float_toLong")
@@ -1143,14 +1153,9 @@ public final class Float : Number(), Comparable<Float> {
@SymbolName("Kotlin_Float_toDouble") @SymbolName("Kotlin_Float_toDouble")
external public override fun toDouble(): Double external public override fun toDouble(): Double
// Konan-specific. public fun equals(other: Float): Boolean = toBits() == other.toBits()
// 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 override fun equals(other: Any?): Boolean = public override fun equals(other: Any?): Boolean = other is Float && this.equals(other)
other is Float && konan.internal.areEqualByValue(this, other)
public override fun toString() = NumberConverter.convert(this) 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Byte") public operator fun compareTo(other: Byte): Int = compareTo(other.toDouble())
external public operator fun compareTo(other: Byte): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Short") public operator fun compareTo(other: Short): Int = compareTo(other.toDouble())
external public operator fun compareTo(other: Short): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Int") public operator fun compareTo(other: Int): Int = compareTo(other.toDouble())
external public operator fun compareTo(other: Int): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Long") public operator fun compareTo(other: Long): Int = compareTo(other.toDouble())
external public operator fun compareTo(other: Long): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Float") public operator fun compareTo(other: Float): Int = compareTo(other.toDouble())
external public operator fun compareTo(other: Float): Int
/** /**
* Compares this value with the specified value for order. * 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, * 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. * or a positive number if its greater than other.
*/ */
@SymbolName("Kotlin_Double_compareTo_Double") public override operator fun compareTo(other: Double): Int {
external 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. */ /** Adds the other value to this value. */
@SymbolName("Kotlin_Double_plus_Byte") @SymbolName("Kotlin_Double_plus_Byte")
@@ -1347,14 +1365,14 @@ public final class Double : Number(), Comparable<Double> {
@SymbolName("Kotlin_Double_unaryMinus") @SymbolName("Kotlin_Double_unaryMinus")
external public operator fun unaryMinus(): Double 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() public override fun toChar(): Char = this.toInt().toChar()
@SymbolName("Kotlin_Double_toShort")
external public override fun toShort(): Short public override fun toShort(): Short = this.toInt().toShort()
@SymbolName("Kotlin_Double_toInt")
external public override fun toInt(): Int public override fun toInt(): Int = this.toLong().toInt()
@SymbolName("Kotlin_Double_toLong") @SymbolName("Kotlin_Double_toLong")
external public override fun toLong(): Long external public override fun toLong(): Long
@SymbolName("Kotlin_Double_toFloat") @SymbolName("Kotlin_Double_toFloat")
@@ -1362,19 +1380,13 @@ public final class Double : Number(), Comparable<Double> {
@SymbolName("Kotlin_Double_toDouble") @SymbolName("Kotlin_Double_toDouble")
external public override fun toDouble(): Double external public override fun toDouble(): Double
// Konan-specific. public fun equals(other: Double): Boolean = toBits() == other.toBits()
// 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 override fun equals(other: Any?): Boolean = public override fun equals(other: Any?): Boolean = other is Double && this.equals(other)
other is Double && konan.internal.areEqualByValue(this, other)
public override fun toString() = NumberConverter.convert(this) public override fun toString() = NumberConverter.convert(this)
public override fun hashCode(): Int { public override fun hashCode(): Int = bits().hashCode()
return bits().hashCode()
}
@SymbolName("Kotlin_Double_bits") @SymbolName("Kotlin_Double_bits")
external public fun bits(): Long 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. * considering `null` value greater than any other value.
*/ */
public fun <T: Any> nullsLast(comparator: Comparator<in T>): Comparator<T?> { 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 { override fun compare(a: T?, b: T?): Int {
if (a === b) return 0 if (a === b) return 0
if (a == null) return 1 if (a == null) return 1