Return immaterial values from comparison intrinsics
This commit is contained in:
-49
@@ -7,9 +7,7 @@ package org.jetbrains.kotlin.backend.jvm.codegen
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.descriptors.propertyIfAccessor
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.ComparisonIntrinsic
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicFunction
|
||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||
import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.isNegation
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
@@ -739,53 +737,6 @@ class ExpressionCodegen(
|
||||
jumpIfFalse = !jumpIfFalse
|
||||
}
|
||||
|
||||
// Do not materialize null constants to check for null. Instead use the JVM bytecode
|
||||
// ifnull and ifnonnull instructions.
|
||||
if (condition is IrCall
|
||||
&& condition.symbol == classCodegen.context.irBuiltIns.eqeqSymbol
|
||||
&& (condition.getValueArgument(0)!!.isNullConst() || condition.getValueArgument(1)!!.isNullConst())
|
||||
) {
|
||||
val left = condition.getValueArgument(0)!!
|
||||
val right = condition.getValueArgument(1)!!
|
||||
(if (left.isNullConst()) right else left).accept(this, data).materialize()
|
||||
if (jumpIfFalse) {
|
||||
mv.ifnonnull(jumpToLabel)
|
||||
} else {
|
||||
mv.ifnull(jumpToLabel)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// For comparison intrinsics, branch directly based on the comparison instead of
|
||||
// materializing a boolean and performing and extra jump.
|
||||
if (condition is IrCall) {
|
||||
val intrinsic = classCodegen.context.irIntrinsics.getIntrinsic(condition.descriptor.original as CallableMemberDescriptor)
|
||||
if (intrinsic is ComparisonIntrinsic) {
|
||||
val callable = resolveToCallable(condition, false)
|
||||
(callable as IrIntrinsicFunction).loadArguments(this, data)
|
||||
val stackValue = intrinsic.genStackValue(condition, classCodegen.context)
|
||||
BranchedValue.condJump(stackValue, jumpToLabel, jumpIfFalse, mv)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// For instance of type operators, branch directly on the instanceof result instead
|
||||
// of materializing a boolean and performing an extra jump.
|
||||
// TODO after redundant materialization is removed, this will be unnecessary
|
||||
if (condition is IrTypeOperatorCall &&
|
||||
(condition.operator == IrTypeOperator.NOT_INSTANCEOF || condition.operator == IrTypeOperator.INSTANCEOF)
|
||||
) {
|
||||
val asmType = condition.typeOperand.toKotlinType().asmType
|
||||
condition.argument.accept(this, data).coerce(AsmTypes.OBJECT_TYPE).materialize()
|
||||
val type = boxType(asmType)
|
||||
generateIsCheck(mv, condition.typeOperand.toKotlinType(), type, state.languageVersionSettings.isReleaseCoroutines())
|
||||
if (condition.operator == IrTypeOperator.NOT_INSTANCEOF) {
|
||||
jumpIfFalse = !jumpIfFalse
|
||||
}
|
||||
BranchedValue.condJump(onStack(Type.BOOLEAN_TYPE), jumpToLabel, jumpIfFalse, mv)
|
||||
return
|
||||
}
|
||||
|
||||
BranchedValue.condJump(condition.accept(this, data), jumpToLabel, jumpIfFalse, mv)
|
||||
}
|
||||
|
||||
|
||||
+11
-15
@@ -17,6 +17,8 @@
|
||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.comparisonOperandType
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
@@ -63,18 +65,7 @@ class CompareTo : IntrinsicMethod() {
|
||||
class PrimitiveComparison(
|
||||
private val primitiveNumberType: KotlinType,
|
||||
private val operatorToken: KtSingleValueToken
|
||||
) : IntrinsicMethod(), ComparisonIntrinsic {
|
||||
|
||||
override fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue {
|
||||
val parameterType = context.state.typeMapper.mapType(primitiveNumberType)
|
||||
|
||||
return StackValue.cmp(
|
||||
operatorToken,
|
||||
parameterType,
|
||||
StackValue.onStack(parameterType, primitiveNumberType),
|
||||
StackValue.onStack(parameterType, primitiveNumberType)
|
||||
)
|
||||
}
|
||||
) : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrMemberAccessExpression,
|
||||
@@ -84,10 +75,15 @@ class PrimitiveComparison(
|
||||
val parameterType = context.state.typeMapper.mapType(primitiveNumberType)
|
||||
|
||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
genStackValue(expression, context).put(Type.BOOLEAN_TYPE, v)
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
loadArguments(codegen, data)
|
||||
return StackValue.cmp(
|
||||
operatorToken,
|
||||
parameterType,
|
||||
StackValue.onStack(parameterType, primitiveNumberType),
|
||||
StackValue.onStack(parameterType, primitiveNumberType)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
|
||||
import com.intellij.psi.tree.IElementType
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.BlockInfo
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
@@ -14,71 +16,50 @@ import org.jetbrains.kotlin.codegen.intrinsics.IntrinsicMethods
|
||||
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.ir.util.isNullConst
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsic {
|
||||
|
||||
private fun argumentTypes(
|
||||
expression: IrMemberAccessExpression,
|
||||
context: JvmBackendContext
|
||||
): Pair<Type, Type> {
|
||||
val receiverAndArgs = expression.receiverAndArgs().apply {
|
||||
assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" }
|
||||
}
|
||||
|
||||
var leftType = context.state.typeMapper.mapType(receiverAndArgs.first().type.toKotlinType())
|
||||
var rightType = context.state.typeMapper.mapType(receiverAndArgs.last().type.toKotlinType())
|
||||
|
||||
if (isPrimitive(leftType) != isPrimitive(rightType)) {
|
||||
leftType = boxType(leftType)
|
||||
rightType = boxType(rightType)
|
||||
}
|
||||
return Pair(leftType, rightType)
|
||||
}
|
||||
|
||||
override fun genStackValue(
|
||||
expression: IrMemberAccessExpression,
|
||||
context: JvmBackendContext
|
||||
): StackValue {
|
||||
val (leftType, rightType) = argumentTypes(expression, context)
|
||||
val opToken = expression.origin
|
||||
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))
|
||||
}
|
||||
}
|
||||
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||
|
||||
override fun toCallable(
|
||||
expression: IrMemberAccessExpression,
|
||||
signature: JvmMethodSignature,
|
||||
context: JvmBackendContext
|
||||
): IrIntrinsicFunction {
|
||||
val (leftType, rightType) = argumentTypes(expression, context).let {
|
||||
if (it.first.isFloatingPoint && it.second.isFloatingPoint)
|
||||
Pair(OBJECT_TYPE, OBJECT_TYPE)
|
||||
else
|
||||
it
|
||||
val receiverAndArgs = expression.receiverAndArgs().apply {
|
||||
assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" }
|
||||
}
|
||||
|
||||
val (leftArg, rightArg) = receiverAndArgs
|
||||
var leftType = context.state.typeMapper.mapType(leftArg.type.toKotlinType())
|
||||
var rightType = context.state.typeMapper.mapType(rightArg.type.toKotlinType())
|
||||
if (isPrimitive(leftType) != isPrimitive(rightType)) {
|
||||
leftType = boxType(leftType)
|
||||
rightType = boxType(rightType)
|
||||
}
|
||||
|
||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) {
|
||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
||||
val value = genStackValue(expression, context)
|
||||
value.put(Type.BOOLEAN_TYPE, v)
|
||||
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||
val opToken = expression.origin
|
||||
return if (leftArg.isNullConst() || rightArg.isNullConst()) {
|
||||
val other = if (leftArg.isNullConst()) rightArg else leftArg
|
||||
StackValue.compareWithNull(other.accept(codegen, data), Opcodes.IFNONNULL)
|
||||
} else if (leftType.isFloatingPoint && rightType.isFloatingPoint) {
|
||||
genEqualsBoxedOnStack(operator)
|
||||
} else 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, codegen.gen(leftArg, operandType, data), codegen.gen(rightArg, operandType, data))
|
||||
} else {
|
||||
genEqualsForExpressionsOnStack(operator, codegen.gen(leftArg, leftType, data), codegen.gen(rightArg, rightType, data))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
||||
import org.jetbrains.kotlin.codegen.Callable
|
||||
import org.jetbrains.kotlin.codegen.CallableMethod
|
||||
import org.jetbrains.kotlin.codegen.StackValue
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||
@@ -16,10 +15,6 @@ import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||
|
||||
interface ComparisonIntrinsic {
|
||||
fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue
|
||||
}
|
||||
|
||||
abstract class IntrinsicMethod {
|
||||
|
||||
open fun toCallable(
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun equals3(a: Char?, b: Char?) = a != null && b != null && a == b
|
||||
|
||||
fun equals4(a: Char?, b: Char?) = if (a is Char && b is Char) a == b else null!!
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// IGNORE_BACKEND: JVM_IR
|
||||
fun equals3(a: Long?, b: Long?) = a != null && b != null && a == b
|
||||
|
||||
fun equals4(a: Long?, b: Long?) = if (a is Long && b is Long) a == b else null!!
|
||||
|
||||
Reference in New Issue
Block a user