[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:
Kristoffer Andersen
2019-10-18 12:24:10 +02:00
committed by Alexander Udalov
parent 0675b54e11
commit 8561f08f06
17 changed files with 63 additions and 21 deletions
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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,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
View File
@@ -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