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:
committed by
max-kammerer
parent
9ddb64288c
commit
9fc869397d
@@ -848,19 +848,30 @@ public class AsmUtil {
|
|||||||
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
|
return StackValue.operation(Type.BOOLEAN_TYPE, v -> {
|
||||||
left.put(AsmTypes.OBJECT_TYPE, left.kotlinType, v);
|
left.put(AsmTypes.OBJECT_TYPE, left.kotlinType, v);
|
||||||
right.put(AsmTypes.OBJECT_TYPE, right.kotlinType, v);
|
right.put(AsmTypes.OBJECT_TYPE, right.kotlinType, v);
|
||||||
genAreEqualCall(v);
|
return genAreEqualCall(v, opToken);
|
||||||
|
|
||||||
if (opToken == KtTokens.EXCLEQ || opToken == KtTokens.EXCLEQEQEQ) {
|
|
||||||
genInvertBoolean(v);
|
|
||||||
}
|
|
||||||
return Unit.INSTANCE;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
public static StackValue genEqualsBoxedOnStack(@NotNull IElementType opToken) {
|
||||||
|
return StackValue.operation(Type.BOOLEAN_TYPE, v -> genAreEqualCall(v, opToken));
|
||||||
|
}
|
||||||
|
|
||||||
public static void genAreEqualCall(InstructionAdapter v) {
|
public static void genAreEqualCall(InstructionAdapter v) {
|
||||||
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", "(Ljava/lang/Object;Ljava/lang/Object;)Z", false);
|
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) {
|
public static void genIEEE754EqualForNullableTypesCall(InstructionAdapter v, Type left, Type right) {
|
||||||
v.invokestatic(IntrinsicMethods.INTRINSICS_CLASS_NAME, "areEqual", "(" + left.getDescriptor() + right.getDescriptor() + ")Z", false);
|
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.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
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.AsmTypes.OBJECT_TYPE
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||||
@@ -49,12 +48,18 @@ class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsi
|
|||||||
): StackValue {
|
): StackValue {
|
||||||
val (leftType, rightType) = argumentTypes(expression, context)
|
val (leftType, rightType) = argumentTypes(expression, context)
|
||||||
val opToken = expression.origin
|
val opToken = expression.origin
|
||||||
return if (opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ) {
|
return when {
|
||||||
// TODO: always casting to the type of the left operand in case of primitives looks wrong
|
leftType.isFloatingPoint && rightType.isFloatingPoint ->
|
||||||
val operandType = if (isPrimitive(leftType)) leftType else OBJECT_TYPE
|
genEqualsBoxedOnStack(operator)
|
||||||
StackValue.cmp(operator, operandType, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
|
||||||
} else {
|
opToken === IrStatementOrigin.EQEQEQ || opToken === IrStatementOrigin.EXCLEQEQ -> {
|
||||||
genEqualsForExpressionsOnStack(operator, StackValue.onStack(leftType), StackValue.onStack(rightType))
|
// 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,
|
signature: JvmMethodSignature,
|
||||||
context: JvmBackendContext
|
context: JvmBackendContext
|
||||||
): IrIntrinsicFunction {
|
): 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)) {
|
return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) {
|
||||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
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
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+1
-6
@@ -95,12 +95,7 @@ public class IntrinsicMethods {
|
|||||||
for (PrimitiveType type : PrimitiveType.values()) {
|
for (PrimitiveType type : PrimitiveType.values()) {
|
||||||
FqName typeFqName = type.getTypeFqName();
|
FqName typeFqName = type.getTypeFqName();
|
||||||
Type asmPrimitiveType = AsmTypes.valueTypeForPrimitive(type);
|
Type asmPrimitiveType = AsmTypes.valueTypeForPrimitive(type);
|
||||||
if (asmPrimitiveType == Type.FLOAT_TYPE || asmPrimitiveType == Type.DOUBLE_TYPE) {
|
declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS);
|
||||||
declareIntrinsicFunction(typeFqName, "equals", 1, new TotalOrderEquals(asmPrimitiveType));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
declareIntrinsicFunction(typeFqName, "equals", 1, EQUALS);
|
|
||||||
}
|
|
||||||
declareIntrinsicFunction(typeFqName, "hashCode", 0, HASH_CODE);
|
declareIntrinsicFunction(typeFqName, "hashCode", 0, HASH_CODE);
|
||||||
declareIntrinsicFunction(typeFqName, "toString", 0, TO_STRING);
|
declareIntrinsicFunction(typeFqName, "toString", 0, TO_STRING);
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR
|
// IGNORE_BACKEND: JS_IR
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
// IGNORE_BACKEND: JS_IR
|
// IGNORE_BACKEND: JS_IR
|
||||||
// TODO: muted automatically, investigate should it be ran for JS or not
|
// TODO: muted automatically, investigate should it be ran for JS or not
|
||||||
// IGNORE_BACKEND: JS
|
// IGNORE_BACKEND: JS
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
data class Test(val z1: Double, val z2: Double?)
|
data class Test(val z1: Double, val z2: Double?)
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
|
|||||||
Reference in New Issue
Block a user