Fuse primitive equality with safe call to avoid boxing
In code like 'a?.b == 42', we can immediately generate equality comparison result when receiver is null (false for '==', true for '!='), since the primitive value is definitely non-null. Otherwise unnecessary boxing/unboxing is generated to handle possibly null result of 'a?.b'.
This commit is contained in:
@@ -209,33 +209,9 @@ class NumberCompare(
|
||||
right: StackValue
|
||||
) : BranchedValue(left, right, operandType, NumberCompare.getNumberCompareOpcode(opToken)) {
|
||||
|
||||
override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int {
|
||||
// Opcode takes one int operand from the stack
|
||||
assert(opcode in IFEQ..IFLE) {
|
||||
"Opcode for comparing must be in range ${IFEQ..IFLE}, but $opcode was found"
|
||||
}
|
||||
override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int =
|
||||
patchOpcode(opcode, v, opToken, operandType)
|
||||
|
||||
return when (operandType) {
|
||||
Type.FLOAT_TYPE, Type.DOUBLE_TYPE -> {
|
||||
if (opToken == KtTokens.GT || opToken == KtTokens.GTEQ) {
|
||||
v.cmpl(operandType)
|
||||
}
|
||||
else {
|
||||
v.cmpg(operandType)
|
||||
}
|
||||
|
||||
opcode
|
||||
}
|
||||
Type.LONG_TYPE -> {
|
||||
v.lcmp()
|
||||
|
||||
opcode
|
||||
}
|
||||
else -> {
|
||||
opcode + (IF_ICMPEQ - IFEQ)
|
||||
}
|
||||
}
|
||||
}
|
||||
companion object {
|
||||
fun getNumberCompareOpcode(opToken: IElementType): Int {
|
||||
return when (opToken) {
|
||||
@@ -250,6 +226,27 @@ class NumberCompare(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun patchOpcode(opcode: Int, v: InstructionAdapter, opToken: IElementType, operandType: Type): Int {
|
||||
assert(opcode in IFEQ..IFLE) {
|
||||
"Opcode for comparing must be in range ${IFEQ..IFLE}, but $opcode was found"
|
||||
}
|
||||
return when (operandType) {
|
||||
Type.FLOAT_TYPE, Type.DOUBLE_TYPE -> {
|
||||
if (opToken == KtTokens.GT || opToken == KtTokens.GTEQ)
|
||||
v.cmpl(operandType)
|
||||
else
|
||||
v.cmpg(operandType)
|
||||
opcode
|
||||
}
|
||||
Type.LONG_TYPE -> {
|
||||
v.lcmp()
|
||||
opcode
|
||||
}
|
||||
else ->
|
||||
opcode + (IF_ICMPEQ - IFEQ)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,3 +267,91 @@ class ObjectCompare(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SafeCallFusedWithPrimitiveEqualityBase(
|
||||
val opToken: IElementType,
|
||||
operandType: Type,
|
||||
left: StackValue,
|
||||
right: StackValue
|
||||
) : BranchedValue(left, right, operandType, NumberCompare.getNumberCompareOpcode(opToken)) {
|
||||
private val trueIfEqual = opToken == KtTokens.EQEQ || opToken == KtTokens.EQEQEQ
|
||||
|
||||
protected abstract fun cleanupOnNullReceiver(v: InstructionAdapter)
|
||||
|
||||
override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int =
|
||||
NumberCompare.patchOpcode(opcode, v, opToken, operandType)
|
||||
|
||||
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
|
||||
val endLabel = Label()
|
||||
|
||||
arg1.put(operandType, v)
|
||||
arg2!!.put(operandType, v)
|
||||
v.visitJumpInsn(patchOpcode(if (jumpIfFalse) opcode else negatedOperations[opcode]!!, v), jumpLabel)
|
||||
v.goTo(endLabel)
|
||||
|
||||
cleanupOnNullReceiver(v)
|
||||
if (jumpIfFalse == trueIfEqual) {
|
||||
v.goTo(jumpLabel)
|
||||
}
|
||||
|
||||
v.mark(endLabel)
|
||||
}
|
||||
|
||||
override fun putSelector(type: Type, v: InstructionAdapter) {
|
||||
val falseLabel = Label()
|
||||
val endLabel = Label()
|
||||
|
||||
arg1.put(operandType, v)
|
||||
arg2!!.put(operandType, v)
|
||||
v.visitJumpInsn(patchOpcode(opcode, v), falseLabel)
|
||||
|
||||
if (!trueIfEqual) {
|
||||
val trueLabel = Label()
|
||||
v.goTo(trueLabel)
|
||||
cleanupOnNullReceiver(v)
|
||||
v.mark(trueLabel)
|
||||
}
|
||||
|
||||
v.iconst(1)
|
||||
v.goTo(endLabel)
|
||||
|
||||
if (trueIfEqual) {
|
||||
cleanupOnNullReceiver(v)
|
||||
}
|
||||
|
||||
v.mark(falseLabel)
|
||||
v.iconst(0)
|
||||
|
||||
v.mark(endLabel)
|
||||
coerceTo(type, v)
|
||||
}
|
||||
}
|
||||
|
||||
class SafeCallToPrimitiveEquality(
|
||||
opToken: IElementType,
|
||||
operandType: Type,
|
||||
left: StackValue,
|
||||
right: StackValue,
|
||||
val safeReceiverType: Type,
|
||||
val safeReceiverIsNull: Label
|
||||
) : SafeCallFusedWithPrimitiveEqualityBase(opToken, operandType, left, right) {
|
||||
override fun cleanupOnNullReceiver(v: InstructionAdapter) {
|
||||
v.mark(safeReceiverIsNull)
|
||||
AsmUtil.pop(v, safeReceiverType)
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveToSafeCallEquality(
|
||||
opToken: IElementType,
|
||||
operandType: Type,
|
||||
left: StackValue,
|
||||
right: StackValue,
|
||||
val safeReceiverType: Type,
|
||||
val safeReceiverIsNull: Label
|
||||
) : SafeCallFusedWithPrimitiveEqualityBase(opToken, operandType, left, right) {
|
||||
override fun cleanupOnNullReceiver(v: InstructionAdapter) {
|
||||
v.mark(safeReceiverIsNull)
|
||||
AsmUtil.pop(v, safeReceiverType)
|
||||
AsmUtil.pop(v, arg1.type)
|
||||
}
|
||||
}
|
||||
@@ -2927,6 +2927,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return genCmpWithZero(left, opToken);
|
||||
}
|
||||
|
||||
if (left instanceof KtSafeQualifiedExpression && isPrimitive(rightType)) {
|
||||
return genCmpSafeCallToPrimitive((KtSafeQualifiedExpression) left, right, rightType, opToken);
|
||||
}
|
||||
if (isPrimitive(leftType) && right instanceof KtSafeQualifiedExpression) {
|
||||
return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken);
|
||||
}
|
||||
|
||||
if (isPrimitive(leftType) != isPrimitive(rightType)) {
|
||||
leftType = boxType(leftType);
|
||||
rightType = boxType(rightType);
|
||||
@@ -2941,6 +2948,40 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
return genEqualsForExpressionsPreferIEEE754Arithmetic(left, right, opToken, leftType, rightType, null);
|
||||
}
|
||||
|
||||
private StackValue genCmpPrimitiveToSafeCall(
|
||||
@NotNull KtExpression left,
|
||||
@NotNull Type leftType,
|
||||
@NotNull KtSafeQualifiedExpression right,
|
||||
@NotNull IElementType opToken
|
||||
) {
|
||||
Label rightIsNull = new Label();
|
||||
return new PrimitiveToSafeCallEquality(
|
||||
opToken,
|
||||
leftType,
|
||||
genLazy(left, leftType),
|
||||
generateSafeQualifiedExpression(right, rightIsNull),
|
||||
expressionType(right.getReceiverExpression()),
|
||||
rightIsNull
|
||||
);
|
||||
}
|
||||
|
||||
private StackValue genCmpSafeCallToPrimitive(
|
||||
@NotNull KtSafeQualifiedExpression left,
|
||||
@NotNull KtExpression right,
|
||||
@NotNull Type rightType,
|
||||
@NotNull IElementType opToken
|
||||
) {
|
||||
Label leftIsNull = new Label();
|
||||
return new SafeCallToPrimitiveEquality(
|
||||
opToken,
|
||||
rightType,
|
||||
generateSafeQualifiedExpression(left, leftIsNull),
|
||||
genLazy(right, rightType),
|
||||
expressionType(left.getReceiverExpression()),
|
||||
leftIsNull
|
||||
);
|
||||
}
|
||||
|
||||
/*tries to use IEEE 754 arithmetic*/
|
||||
private StackValue genEqualsForExpressionsPreferIEEE754Arithmetic(
|
||||
@Nullable KtExpression left,
|
||||
|
||||
Reference in New Issue
Block a user