Return immaterial values from comparison intrinsics

This commit is contained in:
pyos
2019-03-28 10:11:01 +01:00
committed by max-kammerer
parent 9aff1cefb4
commit 31c7183781
6 changed files with 40 additions and 119 deletions
@@ -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)
}
@@ -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))
}
}
}
}
@@ -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!!