[JVM_IR] Avoid some boxing when comparing boxed primitives to primitives.
This commit is contained in:
+37
-1
@@ -22,6 +22,7 @@ import org.jetbrains.kotlin.backend.jvm.codegen.*
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isSmartcastFromHigherThanNullable
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.unboxType
|
||||
import org.jetbrains.kotlin.codegen.BranchedValue
|
||||
import org.jetbrains.kotlin.codegen.NumberCompare
|
||||
import org.jetbrains.kotlin.codegen.ObjectCompare
|
||||
@@ -68,7 +69,7 @@ object CompareTo : IntrinsicMethod() {
|
||||
}
|
||||
}
|
||||
|
||||
class IntegerZeroComparison(val op: IElementType, val a: MaterialValue): BooleanValue(a.codegen) {
|
||||
class IntegerZeroComparison(val op: IElementType, val a: MaterialValue) : BooleanValue(a.codegen) {
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
mv.visitJumpInsn(Opcodes.IFNE, target)
|
||||
}
|
||||
@@ -134,6 +135,41 @@ class NonIEEE754FloatComparison(val op: IElementType, val a: MaterialValue, val
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveToBoxedComparison(
|
||||
val op: IElementType,
|
||||
private val boxedValue: MaterialValue,
|
||||
private val loadOther: () -> MaterialValue
|
||||
) : BooleanValue(boxedValue.codegen) {
|
||||
|
||||
override fun jumpIfFalse(target: Label) {
|
||||
val compareLabel = Label()
|
||||
mv.dup()
|
||||
mv.ifnonnull(compareLabel)
|
||||
mv.pop()
|
||||
mv.goTo(target)
|
||||
mv.mark(compareLabel)
|
||||
val unboxedValue = boxedValue.materializedAt(unboxType(boxedValue.type), boxedValue.irType)
|
||||
BooleanComparison(op, unboxedValue, loadOther()).jumpIfFalse(target)
|
||||
}
|
||||
|
||||
override fun jumpIfTrue(target: Label) {
|
||||
val compareLabel = Label()
|
||||
val endLabel = Label()
|
||||
mv.dup()
|
||||
mv.ifnonnull(compareLabel)
|
||||
mv.pop()
|
||||
mv.goTo(endLabel)
|
||||
mv.mark(compareLabel)
|
||||
val unboxedValue = boxedValue.materializedAt(unboxType(boxedValue.type), boxedValue.irType)
|
||||
BooleanComparison(op, unboxedValue, loadOther()).jumpIfTrue(target)
|
||||
mv.mark(endLabel)
|
||||
}
|
||||
|
||||
override fun discard() {
|
||||
boxedValue.discard()
|
||||
}
|
||||
}
|
||||
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: KotlinType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil.isPrimitive
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||
import org.jetbrains.kotlin.ir.types.classOrNull
|
||||
@@ -25,6 +26,7 @@ import org.jetbrains.kotlin.ir.util.isEnumEntry
|
||||
import org.jetbrains.kotlin.ir.util.isIntegerConst
|
||||
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi2ir.generators.hasNoSideEffects
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
@@ -80,6 +82,38 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
val leftType = with(codegen) { a.asmType }
|
||||
val rightType = with(codegen) { b.asmType }
|
||||
val opToken = expression.origin
|
||||
|
||||
fun loadOther(expression: IrExpression, type: Type): () -> MaterialValue {
|
||||
return if (expression.hasNoSideEffects()) {
|
||||
{ expression.accept(codegen, data).materializedAt(type, expression.type) }
|
||||
} else {
|
||||
val aValue = expression.accept(codegen, data).materializedAt(type, expression.type)
|
||||
val local = codegen.frameMap.enterTemp(type)
|
||||
codegen.mv.store(local, type)
|
||||
({
|
||||
codegen.mv.load(local, type)
|
||||
codegen.frameMap.leaveTemp(type)
|
||||
aValue
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if ((opToken == IrStatementOrigin.EQEQ || opToken == IrStatementOrigin.EXCLEQ) &&
|
||||
(AsmUtil.isIntOrLongPrimitive(leftType) || AsmUtil.isIntOrLongPrimitive(rightType)) &&
|
||||
(AsmUtil.isBoxedTypeOf(leftType, rightType) || AsmUtil.isBoxedTypeOf(rightType, leftType))
|
||||
) {
|
||||
val leftIsPrimitive = AsmUtil.isIntOrLongPrimitive(leftType)
|
||||
return if (leftIsPrimitive) {
|
||||
val loadOther = loadOther(a, leftType)
|
||||
val boxedValue = b.accept(codegen, data).materializedAt(rightType, b.type)
|
||||
PrimitiveToBoxedComparison(operator, boxedValue, loadOther)
|
||||
} else {
|
||||
val boxedValue = a.accept(codegen, data).materializedAt(leftType, a.type)
|
||||
val loadOther = loadOther(b, rightType)
|
||||
PrimitiveToBoxedComparison(operator, boxedValue, loadOther)
|
||||
}
|
||||
}
|
||||
|
||||
val aIsEnum = a.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
||||
val bIsEnum = b.type.classOrNull?.owner?.run { isEnumClass || isEnumEntry } == true
|
||||
val useEquals = opToken !== IrStatementOrigin.EQEQEQ && opToken !== IrStatementOrigin.EXCLEQEQ &&
|
||||
|
||||
@@ -408,7 +408,8 @@ fun IrExpression.hasNoSideEffects() =
|
||||
this is IrFunctionExpression ||
|
||||
(this is IrCallableReference && dispatchReceiver == null && extensionReceiver == null) ||
|
||||
this is IrClassReference ||
|
||||
this is IrConst<*>
|
||||
this is IrConst<*> ||
|
||||
this is IrGetValue
|
||||
|
||||
fun CallGenerator.generateCall(ktElement: KtElement, call: CallBuilder, origin: IrStatementOrigin? = null) =
|
||||
generateCall(ktElement.startOffsetSkippingComments, ktElement.endOffset, call, origin)
|
||||
|
||||
+4
-3
@@ -45,9 +45,7 @@ import org.jetbrains.kotlin.resolve.checkers.PrimitiveNumericComparisonInfo
|
||||
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.checker.intersectTypes
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNotNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
|
||||
|
||||
class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : StatementGeneratorExtension(statementGenerator) {
|
||||
@@ -390,6 +388,9 @@ class OperatorExpressionGenerator(statementGenerator: StatementGenerator) : Stat
|
||||
operandType == targetType || operandNNType == targetType ->
|
||||
this
|
||||
|
||||
targetType.isInt() && (operandNNType.isShort() || operandNNType.isByte()) ->
|
||||
this
|
||||
|
||||
// TODO: don't rely on originalKotlinType.
|
||||
type.originalKotlinType!!.containsNull() ->
|
||||
safeCallOnDispatchReceiver(this@OperatorExpressionGenerator, startOffset, endOffset) { dispatchReceiver ->
|
||||
|
||||
Reference in New Issue
Block a user