Avoid primitive boxing for 'boxed == primitive' if possible

This makes sense for non-floating-point primitive type
(boolean, char, byte, short, int, long):
floating-point types use specialized versions of 'areEqual'.
This commit is contained in:
Dmitry Petrov
2017-07-13 14:15:09 +03:00
parent 275c758da1
commit 8aacddb9f0
17 changed files with 781 additions and 1 deletions
@@ -140,6 +140,10 @@ public class AsmUtil {
return primitiveTypeByBoxedType.get(boxedType);
}
public static boolean isBoxedTypeOf(@NotNull Type boxedType, @NotNull Type unboxedType) {
return unboxPrimitiveTypeOrNull(boxedType) == unboxedType;
}
public static boolean isIntPrimitive(Type type) {
return type == Type.INT_TYPE || type == Type.SHORT_TYPE || type == Type.BYTE_TYPE || type == Type.CHAR_TYPE;
}
@@ -19,6 +19,7 @@ package org.jetbrains.kotlin.codegen
import com.intellij.psi.tree.IElementType
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.org.objectweb.asm.Label
import org.jetbrains.org.objectweb.asm.Opcodes
import org.jetbrains.org.objectweb.asm.Type
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
@@ -112,3 +113,130 @@ class PrimitiveToSafeCallEquality(
}
}
class BoxedToPrimitiveEquality private constructor(
leftBoxed: StackValue,
rightPrimitive: StackValue,
primitiveType: Type
) : BranchedValue(leftBoxed, rightPrimitive, primitiveType, Opcodes.IFNE) {
private val boxedType = arg1.type
override fun patchOpcode(opcode: Int, v: InstructionAdapter): Int =
NumberCompare.patchOpcode(opcode, v, KtTokens.EQEQ, operandType)
override fun condJump(jumpLabel: Label, v: InstructionAdapter, jumpIfFalse: Boolean) {
if (jumpIfFalse) {
jumpIfFalse(v, jumpLabel)
}
else {
jumpIfTrue(v, jumpLabel)
}
}
private fun jumpIfTrue(v: InstructionAdapter, jumpLabel: Label) {
if (arg1.canHaveSideEffects() || arg2!!.canHaveSideEffects()) {
jumpIfTrueWithPossibleSideEffects(v, jumpLabel)
return
}
val notNullLabel = Label()
val endLabel = Label()
arg1.put(boxedType, v)
AsmUtil.dup(v, boxedType)
v.ifnonnull(notNullLabel)
AsmUtil.pop(v, boxedType)
v.goTo(endLabel)
v.mark(notNullLabel)
coerce(boxedType, operandType, v)
arg2.put(operandType, v)
v.visitJumpInsn(patchOpcode(negatedOperations[opcode]!!, v), jumpLabel)
v.mark(endLabel)
}
private fun jumpIfTrueWithPossibleSideEffects(v: InstructionAdapter, jumpLabel: Label) {
val notNullLabel = Label()
val endLabel = Label()
arg1.put(boxedType, v)
arg2!!.put(operandType, v)
AsmUtil.swap(v, operandType, boxedType)
AsmUtil.dup(v, boxedType)
v.ifnonnull(notNullLabel)
AsmUtil.pop(v, boxedType)
AsmUtil.pop(v, operandType)
v.goTo(endLabel)
v.mark(notNullLabel)
coerce(boxedType, operandType, v)
v.visitJumpInsn(patchOpcode(negatedOperations[opcode]!!, v), jumpLabel)
v.mark(endLabel)
}
private fun jumpIfFalse(v: InstructionAdapter, jumpLabel: Label) {
if (arg1.canHaveSideEffects() || arg2!!.canHaveSideEffects()) {
jumpIfFalseWithPossibleSideEffects(v, jumpLabel)
return
}
val notNullLabel = Label()
arg1.put(boxedType, v)
AsmUtil.dup(v, boxedType)
v.ifnonnull(notNullLabel)
AsmUtil.pop(v, boxedType)
v.goTo(jumpLabel)
v.mark(notNullLabel)
coerce(boxedType, operandType, v)
arg2.put(operandType, v)
v.visitJumpInsn(patchOpcode(opcode, v), jumpLabel)
}
private fun jumpIfFalseWithPossibleSideEffects(v: InstructionAdapter, jumpLabel: Label) {
val notNullLabel = Label()
arg1.put(boxedType, v)
arg2!!.put(operandType, v)
AsmUtil.swap(v, operandType, boxedType)
AsmUtil.dup(v, boxedType)
v.ifnonnull(notNullLabel)
AsmUtil.pop(v, boxedType)
AsmUtil.pop(v, operandType)
v.goTo(jumpLabel)
v.mark(notNullLabel)
coerce(boxedType, operandType, v)
v.visitJumpInsn(patchOpcode(opcode, v), jumpLabel)
}
companion object {
@JvmStatic
fun create(opToken: IElementType, leftBoxed: StackValue, rightPrimitive: StackValue, primitiveType: Type): BranchedValue =
if (!isApplicable(opToken, primitiveType))
throw IllegalArgumentException("Not applicable for $opToken, $primitiveType")
else when (opToken) {
KtTokens.EQEQ -> BoxedToPrimitiveEquality(leftBoxed, rightPrimitive, primitiveType)
KtTokens.EXCLEQ -> Invert(BoxedToPrimitiveEquality(leftBoxed, rightPrimitive, primitiveType))
else -> throw AssertionError("Unexpected opToken: $opToken")
}
@JvmStatic
fun isApplicable(opToken: IElementType, primitiveType: Type) =
(opToken == KtTokens.EQEQ ||
opToken == KtTokens.EXCLEQ
) &&
(primitiveType == Type.BOOLEAN_TYPE ||
primitiveType == Type.CHAR_TYPE ||
primitiveType == Type.BYTE_TYPE ||
primitiveType == Type.SHORT_TYPE ||
primitiveType == Type.INT_TYPE ||
primitiveType == Type.LONG_TYPE
)
}
}
@@ -39,12 +39,12 @@ import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenForLambda;
import org.jetbrains.kotlin.codegen.coroutines.CoroutineCodegenUtilKt;
import org.jetbrains.kotlin.codegen.coroutines.ResolvedCallWithRealDescriptor;
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension;
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator;
import org.jetbrains.kotlin.codegen.inline.*;
import org.jetbrains.kotlin.codegen.intrinsics.*;
import org.jetbrains.kotlin.codegen.pseudoInsns.PseudoInsnsKt;
import org.jetbrains.kotlin.codegen.range.RangeValue;
import org.jetbrains.kotlin.codegen.range.RangeValuesKt;
import org.jetbrains.kotlin.codegen.range.forLoop.ForLoopGenerator;
import org.jetbrains.kotlin.codegen.signature.BothSignatureWriter;
import org.jetbrains.kotlin.codegen.signature.JvmSignatureWriter;
import org.jetbrains.kotlin.codegen.state.GenerationState;
@@ -2895,6 +2895,10 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return genCmpPrimitiveToSafeCall(left, leftType, (KtSafeQualifiedExpression) right, opToken);
}
if (isBoxedTypeOf(leftType, rightType) && BoxedToPrimitiveEquality.isApplicable(opToken, rightType)) {
return BoxedToPrimitiveEquality.create(opToken, genLazy(left, leftType), genLazy(right, rightType), rightType);
}
if (isPrimitive(leftType) != isPrimitive(rightType)) {
leftType = boxType(leftType);
rightType = boxType(rightType);