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.common.descriptors.propertyIfAccessor
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin
|
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.IrIntrinsicFunction
|
||||||
import org.jetbrains.kotlin.backend.jvm.intrinsics.IrIntrinsicMethods
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
import org.jetbrains.kotlin.backend.jvm.lower.CrIrType
|
||||||
import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.isNegation
|
import org.jetbrains.kotlin.backend.jvm.lower.JvmBuiltinOptimizationLowering.Companion.isNegation
|
||||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||||
@@ -739,53 +737,6 @@ class ExpressionCodegen(
|
|||||||
jumpIfFalse = !jumpIfFalse
|
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)
|
BranchedValue.condJump(condition.accept(this, data), jumpToLabel, jumpIfFalse, mv)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+11
-15
@@ -17,6 +17,8 @@
|
|||||||
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
package org.jetbrains.kotlin.backend.jvm.intrinsics
|
||||||
|
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
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.AsmUtil.comparisonOperandType
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
import org.jetbrains.kotlin.codegen.StackValue
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||||
@@ -63,18 +65,7 @@ class CompareTo : IntrinsicMethod() {
|
|||||||
class PrimitiveComparison(
|
class PrimitiveComparison(
|
||||||
private val primitiveNumberType: KotlinType,
|
private val primitiveNumberType: KotlinType,
|
||||||
private val operatorToken: KtSingleValueToken
|
private val operatorToken: KtSingleValueToken
|
||||||
) : IntrinsicMethod(), ComparisonIntrinsic {
|
) : IntrinsicMethod() {
|
||||||
|
|
||||||
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)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toCallable(
|
override fun toCallable(
|
||||||
expression: IrMemberAccessExpression,
|
expression: IrMemberAccessExpression,
|
||||||
@@ -84,10 +75,15 @@ class PrimitiveComparison(
|
|||||||
val parameterType = context.state.typeMapper.mapType(primitiveNumberType)
|
val parameterType = context.state.typeMapper.mapType(primitiveNumberType)
|
||||||
|
|
||||||
return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) {
|
return object : IrIntrinsicFunction(expression, signature, context, listOf(parameterType, parameterType)) {
|
||||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||||
genStackValue(expression, context).put(Type.BOOLEAN_TYPE, v)
|
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 com.intellij.psi.tree.IElementType
|
||||||
import org.jetbrains.kotlin.backend.jvm.JvmBackendContext
|
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.AsmUtil.*
|
import org.jetbrains.kotlin.codegen.AsmUtil.*
|
||||||
import org.jetbrains.kotlin.codegen.StackValue
|
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.IrMemberAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
|
import org.jetbrains.kotlin.ir.util.isNullConst
|
||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
import org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE
|
||||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||||
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberOrNullableType
|
||||||
import org.jetbrains.kotlin.types.typeUtil.upperBoundedByPrimitiveNumberOrNullableType
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||||
|
|
||||||
class Equals(val operator: IElementType) : IntrinsicMethod(), ComparisonIntrinsic {
|
class Equals(val operator: IElementType) : IntrinsicMethod() {
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toCallable(
|
override fun toCallable(
|
||||||
expression: IrMemberAccessExpression,
|
expression: IrMemberAccessExpression,
|
||||||
signature: JvmMethodSignature,
|
signature: JvmMethodSignature,
|
||||||
context: JvmBackendContext
|
context: JvmBackendContext
|
||||||
): IrIntrinsicFunction {
|
): IrIntrinsicFunction {
|
||||||
val (leftType, rightType) = argumentTypes(expression, context).let {
|
val receiverAndArgs = expression.receiverAndArgs().apply {
|
||||||
if (it.first.isFloatingPoint && it.second.isFloatingPoint)
|
assert(size == 2) { "Equals expects 2 arguments, but ${joinToString()}" }
|
||||||
Pair(OBJECT_TYPE, OBJECT_TYPE)
|
}
|
||||||
else
|
|
||||||
it
|
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)) {
|
return object : IrIntrinsicFunction(expression, signature, context, listOf(leftType, rightType)) {
|
||||||
override fun genInvokeInstruction(v: InstructionAdapter) {
|
override fun invoke(v: InstructionAdapter, codegen: ExpressionCodegen, data: BlockInfo): StackValue {
|
||||||
val value = genStackValue(expression, context)
|
val opToken = expression.origin
|
||||||
value.put(Type.BOOLEAN_TYPE, v)
|
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.backend.jvm.JvmBackendContext
|
||||||
import org.jetbrains.kotlin.codegen.Callable
|
import org.jetbrains.kotlin.codegen.Callable
|
||||||
import org.jetbrains.kotlin.codegen.CallableMethod
|
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.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
import org.jetbrains.kotlin.ir.expressions.IrMemberAccessExpression
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
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.Type
|
||||||
import org.jetbrains.org.objectweb.asm.commons.Method
|
import org.jetbrains.org.objectweb.asm.commons.Method
|
||||||
|
|
||||||
interface ComparisonIntrinsic {
|
|
||||||
fun genStackValue(expression: IrMemberAccessExpression, context: JvmBackendContext): StackValue
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract class IntrinsicMethod {
|
abstract class IntrinsicMethod {
|
||||||
|
|
||||||
open fun toCallable(
|
open fun toCallable(
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun equals3(a: Char?, b: Char?) = a != null && b != null && a == b
|
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!!
|
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 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!!
|
fun equals4(a: Long?, b: Long?) = if (a is Long && b is Long) a == b else null!!
|
||||||
|
|||||||
Reference in New Issue
Block a user