[JVM_IR] Reintroduce non-IEEE comparisons
- Ieee754Equality and PrimitiveComparisons intrinsics respects absence of -Xproper-ieee754-comparisons, unmuted tests to show.
This commit is contained in:
committed by
Alexander Udalov
parent
0675b54e11
commit
8561f08f06
+37
-1
@@ -19,11 +19,13 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.NumberCompare
|
||||
import org.jetbrains.kotlin.codegen.ObjectCompare
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
||||
@@ -84,6 +86,29 @@ class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: Mater
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
|
||||
private val numberCompareOpcode = NumberCompare.getNumberCompareOpcode(op)
|
||||
|
||||
private fun invokeStaticComparison(type: Type) {
|
||||
when (type) {
|
||||
Type.FLOAT_TYPE -> mv.invokestatic("java/lang/Float", "compare", "(FF)I", false)
|
||||
Type.DOUBLE_TYPE -> mv.invokestatic("java/lang/Double", "compare", "(DD)I", false)
|
||||
else -> throw UnsupportedOperationException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
invokeStaticComparison(a.type)
|
||||
mv.visitJumpInsn(numberCompareOpcode, target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
invokeStaticComparison(a.type)
|
||||
mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target)
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: KotlinType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
@@ -93,6 +118,17 @@ class PrimitiveComparison(
|
||||
val (left, right) = expression.receiverAndArgs()
|
||||
val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized
|
||||
val b = right.accept(codegen, data).coerce(parameterType, right.type).materialized
|
||||
return BooleanComparison(operatorToken, a, b)
|
||||
|
||||
val useNonIEEE754Comparison =
|
||||
!codegen.context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
|
||||
&& (parameterType == Type.FLOAT_TYPE || parameterType == Type.DOUBLE_TYPE)
|
||||
&& (left.isSmartcastFromHigherThanNullable(codegen.context) || right.isSmartcastFromHigherThanNullable(codegen.context))
|
||||
|
||||
return if (useNonIEEE754Comparison) {
|
||||
NonIEEE754FloatComparison(operatorToken, a, b)
|
||||
} else {
|
||||
BooleanComparison(operatorToken, a, b)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.genAreEqualCall
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
@@ -15,8 +16,8 @@ import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.types.isNullable
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
@@ -95,18 +96,28 @@ class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
val arg0Type = expression.getValueArgument(0)!!.type.toKotlinType()
|
||||
val arg0 = expression.getValueArgument(0)!!
|
||||
val arg1 = expression.getValueArgument(1)!!
|
||||
|
||||
val arg0Type = arg0.type.toKotlinType()
|
||||
if (!arg0Type.isPrimitiveNumberOrNullableType() && !arg0Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||
throw AssertionError("Should be primitive or nullable primitive type: $arg0Type")
|
||||
|
||||
val arg1Type = expression.getValueArgument(1)!!.type.toKotlinType()
|
||||
val arg1Type = arg1.type.toKotlinType()
|
||||
if (!arg1Type.isPrimitiveNumberOrNullableType() && !arg1Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||
throw AssertionError("Should be primitive or nullable primitive type: $arg1Type")
|
||||
|
||||
val arg0isNullable = arg0Type.isNullable()
|
||||
val arg1isNullable = arg1Type.isNullable()
|
||||
|
||||
val useNonIEEE754Comparison =
|
||||
!context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
|
||||
&& (arg0.isSmartcastFromHigherThanNullable(context) || arg1.isSmartcastFromHigherThanNullable(context))
|
||||
|
||||
return when {
|
||||
useNonIEEE754Comparison ->
|
||||
Ieee754AreEqual(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)
|
||||
|
||||
!arg0isNullable && !arg1isNullable ->
|
||||
object : IrIntrinsicFunction(expression, signature, context, listOf(operandType, operandType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
@@ -126,3 +137,4 @@ class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,7 @@ import org.jetbrains.kotlin.ir.builders.IrBuilderWithScope
|
||||
import org.jetbrains.kotlin.ir.builders.Scope
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
||||
import org.jetbrains.kotlin.ir.expressions.*
|
||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||
@@ -138,3 +137,12 @@ fun JvmBackendContext.createJvmIrBuilder(
|
||||
|
||||
fun IrDeclaration.isInCurrentModule(): Boolean =
|
||||
getPackageFragment() is IrFile
|
||||
|
||||
// Determine if the IrExpression is smartcast, and if so, if it is cast from higher than nullable target types.
|
||||
// This is needed to pinpoint exceptional treatment of IEEE754 floating point comparisons, where proper IEEE
|
||||
// comparisons are used "if values are statically known to be of primitive numeric types", taken to mean as
|
||||
// "not learned through smartcasting".
|
||||
fun IrExpression.isSmartcastFromHigherThanNullable(context: JvmBackendContext) =
|
||||
this is IrTypeOperatorCall &&
|
||||
operator == IrTypeOperator.IMPLICIT_CAST &&
|
||||
!this.argument.type.isSubtypeOf(type.makeNullable(), context.irBuiltIns)
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun equals1(a: Double, b: Double) = a == b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun equals1(a: Float, b: Float) = a == b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
// WITH_RUNTIME
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun equals1(a: Double, b: Double?) = a == b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun equals1(a: Float, b: Float?) = a == b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun greater1(a: Double, b: Double) = a > b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun greater1(a: Float, b: Float) = a > b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun less1(a: Double, b: Double) = a < b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun less1(a: Float, b: Float) = a < b
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
fun box(): String {
|
||||
|
||||
-1
@@ -1,6 +1,5 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: NATIVE
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
fun eqDI(x: Any?, y: Any?) = x is Double? && y is Int? && x == y
|
||||
fun eqDL(x: Any?, y: Any?) = x is Double? && y is Long? && x == y
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
// !LANGUAGE: -ProperIeee754Comparisons
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||
|
||||
fun box(): String {
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun box(): String {
|
||||
val zero: Any = 0.0
|
||||
val floatZero: Any = -0.0F
|
||||
|
||||
Reference in New Issue
Block a user