[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 com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||||
import org.jetbrains.kotlin.codegen.NumberCompare
|
import org.jetbrains.kotlin.codegen.NumberCompare
|
||||||
import org.jetbrains.kotlin.codegen.ObjectCompare
|
import org.jetbrains.kotlin.codegen.ObjectCompare
|
||||||
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||||
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
import org.jetbrains.kotlin.lexer.KtSingleValueToken
|
||||||
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
|
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(
|
class PrimitiveComparison(
|
||||||
private val primitiveNumberType: KotlinType,
|
private val primitiveNumberType: KotlinType,
|
||||||
private val operatorToken: KtSingleValueToken
|
private val operatorToken: KtSingleValueToken
|
||||||
@@ -93,6 +118,17 @@ class PrimitiveComparison(
|
|||||||
val (left, right) = expression.receiverAndArgs()
|
val (left, right) = expression.receiverAndArgs()
|
||||||
val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized
|
val a = left.accept(codegen, data).coerce(parameterType, left.type).materialized
|
||||||
val b = right.accept(codegen, data).coerce(parameterType, right.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 com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.backend.jvm.codegen.*
|
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
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.genAreEqualCall
|
import org.jetbrains.kotlin.codegen.AsmUtil.genAreEqualCall
|
||||||
import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
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.intrinsics.IntrinsicMethods
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysFalseIfeq
|
||||||
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
import org.jetbrains.kotlin.codegen.pseudoInsns.fakeAlwaysTrueIfeq
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
import org.jetbrains.kotlin.config.LanguageFeature
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.types.isNullable
|
import org.jetbrains.kotlin.ir.types.isNullable
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
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())
|
if (!arg0Type.isPrimitiveNumberOrNullableType() && !arg0Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||||
throw AssertionError("Should be primitive or nullable primitive type: $arg0Type")
|
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())
|
if (!arg1Type.isPrimitiveNumberOrNullableType() && !arg1Type.upperBoundedByPrimitiveNumberOrNullableType())
|
||||||
throw AssertionError("Should be primitive or nullable primitive type: $arg1Type")
|
throw AssertionError("Should be primitive or nullable primitive type: $arg1Type")
|
||||||
|
|
||||||
val arg0isNullable = arg0Type.isNullable()
|
val arg0isNullable = arg0Type.isNullable()
|
||||||
val arg1isNullable = arg1Type.isNullable()
|
val arg1isNullable = arg1Type.isNullable()
|
||||||
|
|
||||||
|
val useNonIEEE754Comparison =
|
||||||
|
!context.state.languageVersionSettings.supportsFeature(LanguageFeature.ProperIeee754Comparisons)
|
||||||
|
&& (arg0.isSmartcastFromHigherThanNullable(context) || arg1.isSmartcastFromHigherThanNullable(context))
|
||||||
|
|
||||||
return when {
|
return when {
|
||||||
|
useNonIEEE754Comparison ->
|
||||||
|
Ieee754AreEqual(AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE)
|
||||||
|
|
||||||
!arg0isNullable && !arg1isNullable ->
|
!arg0isNullable && !arg1isNullable ->
|
||||||
object : IrIntrinsicFunction(expression, signature, context, listOf(operandType, operandType)) {
|
object : IrIntrinsicFunction(expression, signature, context, listOf(operandType, operandType)) {
|
||||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
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.builders.Scope
|
||||||
import org.jetbrains.kotlin.ir.declarations.*
|
import org.jetbrains.kotlin.ir.declarations.*
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.*
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrConst
|
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrClassSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||||
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
|
||||||
@@ -138,3 +137,12 @@ fun JvmBackendContext.createJvmIrBuilder(
|
|||||||
|
|
||||||
fun IrDeclaration.isInCurrentModule(): Boolean =
|
fun IrDeclaration.isInCurrentModule(): Boolean =
|
||||||
getPackageFragment() is IrFile
|
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
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun equals1(a: Double, b: Double) = a == b
|
fun equals1(a: Double, b: Double) = a == b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun equals1(a: Float, b: Float) = a == b
|
fun equals1(a: Float, b: Float) = a == b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
// WITH_RUNTIME
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun equals1(a: Double, b: Double?) = a == b
|
fun equals1(a: Double, b: Double?) = a == b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun equals1(a: Float, b: Float?) = a == b
|
fun equals1(a: Float, b: Float?) = a == b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun greater1(a: Double, b: Double) = a > b
|
fun greater1(a: Double, b: Double) = a > b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun greater1(a: Float, b: Float) = a > b
|
fun greater1(a: Float, b: Float) = a > b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun less1(a: Double, b: Double) = a < b
|
fun less1(a: Double, b: Double) = a < b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun less1(a: Float, b: Float) = a < b
|
fun less1(a: Float, b: Float) = a < b
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND: NATIVE
|
// IGNORE_BACKEND: NATIVE
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
-1
@@ -1,6 +1,5 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: NATIVE
|
// IGNORE_BACKEND: NATIVE
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
fun eqDI(x: Any?, y: Any?) = x is Double? && y is Int? && x == y
|
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
|
fun eqDL(x: Any?, y: Any?) = x is Double? && y is Long? && x == y
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
// !LANGUAGE: -ProperIeee754Comparisons
|
// !LANGUAGE: -ProperIeee754Comparisons
|
||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
// DONT_TARGET_EXACT_BACKEND: JS_IR
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val zero: Any = 0.0
|
val zero: Any = 0.0
|
||||||
val floatZero: Any = -0.0F
|
val floatZero: Any = -0.0F
|
||||||
|
|||||||
Reference in New Issue
Block a user