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
@@ -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);