IrBuiltins: refactoring for equality checks

Previously,
* Equals performs IEEE 754 equality check for floating points and
  byte-to-byte checks for other types, including references.
* Ieee754Equals performs IEEE 754 for primitive types
* TotalOrderEquals performs total order equals to all types, including
  floating points.

Now it is simplified,
* Equals performs total order checks for all types.
* Ieee754Equals performs IEEE 754 for primitive types.
* (TotalOrderEquals is removed.)
This commit is contained in:
Ting-Yuan Huang
2019-04-01 17:04:58 -07:00
committed by max-kammerer
parent 9ddb64288c
commit 9fc869397d
6 changed files with 39 additions and 43 deletions
@@ -848,19 +848,30 @@ public class AsmUtil {
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
left.put(AsmTypes.OBJECT_TYPE, left.kotlinType, v);
right.put(AsmTypes.OBJECT_TYPE, right.kotlinType, v);
genAreEqualCall(v);
if (opToken == KtTokens.EXCLEQ || opToken == KtTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
return Unit.INSTANCE;
return genAreEqualCall(v, opToken);
});
}
@NotNull
public static StackValue genEqualsBoxedOnStack(@NotNull IElementType opToken) {
return StackValue.operation(Type.BOOLEAN_TYPE, v -> genAreEqualCall(v, opToken));
}
public static void genAreEqualCall(InstructionAdapter v) {
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false);
}
@NotNull
private static Unit genAreEqualCall(InstructionAdapter v, @NotNull IElementType opToken) {
genAreEqualCall(v);
if (opToken == KtTokens.EXCLEQ || opToken == KtTokens.EXCLEQEQEQ) {
genInvertBoolean(v);
}
return Unit.INSTANCE;
}
public static void genIEEE754EqualForNullableTypesCall(InstructionAdapter v, Type left, Type right) {
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", "(" + left.getDescriptor() + right.getDescriptor() + ")Z", false);
}
@@ -15,7 +15,6 @@ import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
import org.jetbrains.kotlin.ir.types.toKotlinType
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
@@ -49,12 +48,18 @@ class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsi
): StackValue {
val (leftType, rightType) = argumentTypes(expression, context)
val opToken = expression.origin
return if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) {
// TODO: always casting to the type of the left operand in case of primitives looks wrong
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType))
} else {
genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType))
return when {
leftType.isFloatingPoint && rightType.isFloatingPoint ->
genEqualsBoxedOnStack(operator)
opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ -> {
// TODO: always casting to the type of the left operand in case of primitives looks wrong
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType))
}
else ->
genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType))
}
}
@@ -63,7 +68,12 @@ class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsi
signature: JvmMethodSignature,
context: JvmBackendContext
): IrIntrinsicFunction {
var (leftType, rightType) = argumentTypes(expression, context)
val (leftType, rightType) = argumentTypes(expression, context).let {
if (it.first.isFloatingPoint && it.second.isFloatingPoint)
Pair(OBJECT_TYPE, OBJECT_TYPE)
else
it
}
return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) {
override fun genInvokeInstruction(v: InstructionAdapter) {
@@ -72,6 +82,9 @@ class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsi
}
}
}
private val Type.isFloatingPoint: Boolean
get() = this == Type.DOUBLE_TYPE || this == Type.FLOAT_TYPE
}
@@ -128,23 +141,3 @@ class Ieee754Equals(val operandType: Type) : IntrinsicMethod() {
}
}
}
class TotalOrderEquals(operandType: Type) : IntrinsicMethod() {
private val boxedType = AsmUtil.boxType(operandType)
override fun toCallable(
expression: IrMemberAccessExpression,
signature: JvmMethodSignature,
context: JvmBackendContext
): IrIntrinsicFunction =
object : IrIntrinsicFunction(expression, signature, context, listOf(boxedType, boxedType)) {
override fun genInvokeInstruction(v: InstructionAdapter) {
v.invokestatic(
IntrinsicMethods.INTRINSICS_CLASS_NAME,
"areEqual",
Type.getMethodDescriptor(Type.BOOLEAN_TYPE, AsmTypes.OBJECT_TYPE, AsmTypes.OBJECT_TYPE),
false
)
}
}
}
@@ -95,12 +95,7 @@ public class IntrinsicMethods {
for (PrimitiveType type : PrimitiveType.values()) {
FqName typeFqName = type.getTypeFqName();
Type asmPrimitiveType = AsmTypes.valueTypeForPrimitive(type);
if (asmPrimitiveType == Type.FLOAT_TYPE || asmPrimitiveType == Type.DOUBLE_TYPE) {
declareIntrinsicFunction(typeFqName, "equals", 1, new TotalOrderEquals(asmPrimitiveType));
}
else {
declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS);
}
declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS);
declareIntrinsicFunction(typeFqName, "hashCode", 0, HASH_CODE);
declareIntrinsicFunction(typeFqName, "toString", 0, TO_STRING);
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
// IGNORE_BACKEND: JS_IR
// TODO: muted automatically, investigate should it be ran for JS or not
// IGNORE_BACKEND: JS
-1
View File
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: JVM_IR
data class Test(val z1: Double, val z2: Double?)
fun box(): String {