JVM_IR: Generate more line numbers for intrinsic comparisons.

Otherwise, if complex expressions such as when expressions are
used in combination with the intrinsics we get incorrect stepping
behavior.

^KT-64341 Fixed
This commit is contained in:
Mads Ager
2023-12-13 12:43:05 +01:00
committed by Space Cloud
parent a441a82357
commit e41a1247e2
23 changed files with 723 additions and 54 deletions
@@ -13,6 +13,7 @@ import org.jetbrains.kotlin.codegen.AsmUtil
import org.jetbrains.kotlin.codegen.StackValue
import org.jetbrains.kotlin.ir.declarations.IrTypeParameter
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.symbols.IrTypeParameterSymbol
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.isTypeParameter
@@ -110,6 +111,12 @@ abstract class BooleanValue(codegen: ExpressionCodegen) :
StackValue.coerce(Type.BOOLEAN_TYPE, target, mv)
}
}
fun markLineNumber(expression: IrExpression) {
with(codegen) {
expression.markLineNumber(startOffset = true)
}
}
}
class BooleanConstant(codegen: ExpressionCodegen, val value: Boolean) : BooleanValue(codegen) {
@@ -12,25 +12,41 @@ import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.org.objectweb.asm.Label
object AndAnd : IntrinsicMethod() {
private class BooleanConjunction(val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo) :
BooleanValue(codegen) {
private class BooleanConjunction(
val left: IrExpression,
val right: IrExpression,
codegen: ExpressionCodegen,
val data: BlockInfo
) : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfFalse(target)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfFalse(target)
}
override fun jumpIfTrue(target: Label) {
val stayLabel = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(stayLabel)
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfFalse(stayLabel)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfTrue(target)
mv.visitLabel(stayLabel)
}
override fun discard() {
val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfFalse(end)
arg1.accept(codegen, data).discard()
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfFalse(end)
val rightValue = right.accept(codegen, data)
markLineNumber(right)
rightValue.discard()
mv.visitLabel(end)
}
}
@@ -28,6 +28,7 @@ import org.jetbrains.kotlin.codegen.BranchedValue
import org.jetbrains.kotlin.codegen.NumberCompare
import org.jetbrains.kotlin.codegen.ObjectCompare
import org.jetbrains.kotlin.config.LanguageFeature
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.kotlin.lexer.KtSingleValueToken
import org.jetbrains.kotlin.resolve.jvm.JvmPrimitiveType
@@ -67,27 +68,37 @@ object CompareTo : IntrinsicMethod() {
}
}
class IntegerZeroComparison(val a: MaterialValue) : BooleanValue(a.codegen) {
class IntegerZeroComparison(val nonZeroExpression: IrExpression, val a: MaterialValue) : BooleanValue(a.codegen) {
override fun jumpIfFalse(target: Label) {
markLineNumber(nonZeroExpression)
mv.visitJumpInsn(Opcodes.IFNE, target)
}
override fun jumpIfTrue(target: Label) {
markLineNumber(nonZeroExpression)
mv.visitJumpInsn(Opcodes.IFEQ, target)
}
override fun discard() {
markLineNumber(nonZeroExpression)
a.discard()
}
}
class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: MaterialValue) : BooleanValue(a.codegen) {
class BooleanComparison(
val expression: IrFunctionAccessExpression,
val op: IElementType,
val a: MaterialValue,
val b: MaterialValue
) : BooleanValue(a.codegen) {
override fun jumpIfFalse(target: Label) {
// TODO 1. get rid of the dependency; 2. take `b.type` into account.
val opcode = if (a.type.sort == Type.OBJECT)
ObjectCompare.getObjectCompareOpcode(op)
else
NumberCompare.patchOpcode(NumberCompare.getNumberCompareOpcode(op), mv, op, a.type)
markLineNumber(expression)
mv.visitJumpInsn(opcode, target)
}
@@ -96,17 +107,23 @@ class BooleanComparison(val op: IElementType, val a: MaterialValue, val b: Mater
BranchedValue.negatedOperations[ObjectCompare.getObjectCompareOpcode(op)]!!
else
NumberCompare.patchOpcode(BranchedValue.negatedOperations[NumberCompare.getNumberCompareOpcode(op)]!!, mv, op, a.type)
markLineNumber(expression)
mv.visitJumpInsn(opcode, target)
}
override fun discard() {
markLineNumber(expression)
b.discard()
a.discard()
}
}
class NonIEEE754FloatComparison(op: IElementType, private val a: MaterialValue, private val b: MaterialValue) : BooleanValue(a.codegen) {
class NonIEEE754FloatComparison(
private val expression: IrFunctionAccessExpression,
op: IElementType,
private val a: MaterialValue,
private val b: MaterialValue
) : BooleanValue(a.codegen) {
private val numberCompareOpcode = NumberCompare.getNumberCompareOpcode(op)
private fun invokeStaticComparison(type: Type) {
@@ -118,22 +135,26 @@ class NonIEEE754FloatComparison(op: IElementType, private val a: MaterialValue,
}
override fun jumpIfFalse(target: Label) {
markLineNumber(expression)
invokeStaticComparison(a.type)
mv.visitJumpInsn(numberCompareOpcode, target)
}
override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
invokeStaticComparison(a.type)
mv.visitJumpInsn(BranchedValue.negatedOperations[numberCompareOpcode]!!, target)
}
override fun discard() {
markLineNumber(expression)
b.discard()
a.discard()
}
}
class PrimitiveToObjectComparison(
private val expression: IrFunctionAccessExpression,
private val op: IElementType,
private val leftIsPrimitive: Boolean,
private val left: MaterialValue,
@@ -159,26 +180,31 @@ class PrimitiveToObjectComparison(
mv.mark(compareLabel)
// Type checking OK, can unbox and compare:
return if (leftIsPrimitive) {
BooleanComparison(op, left, right.materializedAt(left.type, right.irType))
BooleanComparison(expression, op, left, right.materializedAt(left.type, right.irType))
} else {
val leftUnboxed = left.materializedAt(right.type, left.irType)
mv.load(tmp, right.type)
codegen.frameMap.leaveTemp(right.type)
BooleanComparison(op, leftUnboxed, right)
BooleanComparison(expression, op, leftUnboxed, right)
}
}
override fun jumpIfFalse(target: Label) {
checkTypeAndCompare(target).jumpIfFalse(target)
markLineNumber(expression)
val comparison = checkTypeAndCompare(target)
comparison.jumpIfFalse(target)
}
override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
val wrongType = Label()
checkTypeAndCompare(wrongType).jumpIfTrue(target)
val comparison = checkTypeAndCompare(wrongType)
comparison.jumpIfTrue(target)
mv.mark(wrongType)
}
override fun discard() {
markLineNumber(expression)
right.discard()
left.discard()
}
@@ -200,9 +226,9 @@ class PrimitiveComparison(
&& (left.isSmartcastFromHigherThanNullable(codegen.context) || right.isSmartcastFromHigherThanNullable(codegen.context))
return if (useNonIEEE754Comparison) {
NonIEEE754FloatComparison(operatorToken, a, b)
NonIEEE754FloatComparison(expression, operatorToken, a, b)
} else {
BooleanComparison(operatorToken, a, b)
BooleanComparison(expression, operatorToken, a, b)
}
}
}
@@ -57,10 +57,21 @@ class ExplicitEquals : IntrinsicMethod() {
class Equals(val operator: IElementType) : IntrinsicMethod() {
private class BooleanNullCheck(val value: PromisedValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.materialize().also { mv.ifnonnull(target) }
override fun jumpIfTrue(target: Label) = value.materialize().also { mv.ifnull(target) }
private class BooleanNullCheck(val expression: IrFunctionAccessExpression, val value: PromisedValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) {
value.materialize()
markLineNumber(expression)
mv.ifnonnull(target)
}
override fun jumpIfTrue(target: Label) {
value.materialize()
markLineNumber(expression)
mv.ifnull(target)
}
override fun discard() {
markLineNumber(expression)
value.discard()
}
}
@@ -71,7 +82,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val irValue = if (a.isNullConst()) b else a
val value = irValue.accept(codegen, data)
return if (!isPrimitive(value.type) && (irValue.type.classOrNull?.owner?.isSingleFieldValueClass != true || irValue.type.isNullable()))
BooleanNullCheck(value)
BooleanNullCheck(expression, value)
else {
value.discard()
BooleanConstant(codegen, false)
@@ -80,7 +91,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val leftType = codegen.typeMapper.mapTypeAsDeclaration(a.type)
if (expression.origin == IrStatementOrigin.EQEQEQ || expression.origin == IrStatementOrigin.EXCLEQEQ) {
return referenceEquals(a, b, leftType, codegen, data)
return referenceEquals(expression, a, b, leftType, codegen, data)
}
val rightType = codegen.typeMapper.mapTypeAsDeclaration(b.type)
@@ -93,7 +104,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
) {
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
return PrimitiveToObjectComparison(operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue)
return PrimitiveToObjectComparison(expression, operator, AsmUtil.isIntOrLongPrimitive(leftType), aValue, bValue)
}
if (isPrimitive(leftType) && leftType == rightType) {
@@ -101,26 +112,29 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
return if (leftType == Type.FLOAT_TYPE || leftType == Type.DOUBLE_TYPE) {
val aValue = a.accept(codegen, data).materializedAt(leftType, a.type)
val bValue = b.accept(codegen, data).materializedAt(rightType, b.type)
return NonIEEE754FloatComparison(operator, aValue, bValue)
return NonIEEE754FloatComparison(expression, operator, aValue, bValue)
} else {
referenceEquals(a, b, leftType, codegen, data)
referenceEquals(expression, a, b, leftType, codegen, data)
}
}
// We can use reference equality for enums, otherwise we fall back to boxed equality.
return when {
a.isEnumValue || b.isEnumValue ->
referenceEquals(a, b, leftType, codegen, data)
referenceEquals(expression, a, b, leftType, codegen, data)
a.isClassValue && b.isClassValue -> {
val leftValue = codegen.generateClassLiteralReference(a, wrapIntoKClass = false, wrapPrimitives = true, data = data)
val rightValue = codegen.generateClassLiteralReference(b, wrapIntoKClass = false, wrapPrimitives = true, data = data)
BooleanComparison(operator, leftValue, rightValue)
BooleanComparison(expression, operator, leftValue, rightValue)
}
else -> {
a.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
b.accept(codegen, data).materializeAt(AsmTypes.OBJECT_TYPE, codegen.context.irBuiltIns.anyNType)
with(codegen) {
expression.markLineNumber(startOffset = true)
}
genAreEqualCall(codegen.mv)
MaterialValue(codegen, Type.BOOLEAN_TYPE, codegen.context.irBuiltIns.booleanType)
}
@@ -128,6 +142,7 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
}
private fun referenceEquals(
expression: IrFunctionAccessExpression,
left: IrExpression,
right: IrExpression,
leftType: Type,
@@ -137,11 +152,11 @@ class Equals(val operator: IElementType) : IntrinsicMethod() {
val operandType = if (!isPrimitive(leftType)) AsmTypes.OBJECT_TYPE else leftType
return if (operandType == Type.INT_TYPE && (left.isIntegerConst(0) || right.isIntegerConst(0))) {
val nonZero = if (left.isIntegerConst(0)) right else left
IntegerZeroComparison(nonZero.accept(codegen, data).materializedAt(operandType, nonZero.type))
IntegerZeroComparison(expression, nonZero.accept(codegen, data).materializedAt(operandType, nonZero.type))
} else {
val leftValue = left.accept(codegen, data).materializedAt(operandType, left.type)
val rightValue = right.accept(codegen, data).materializedAt(operandType, right.type)
BooleanComparison(operator, leftValue, rightValue)
BooleanComparison(expression, operator, leftValue, rightValue)
}
}
@@ -22,16 +22,26 @@ import org.jetbrains.kotlin.backend.jvm.codegen.ExpressionCodegen
import org.jetbrains.kotlin.backend.jvm.codegen.coerceToBoolean
import org.jetbrains.kotlin.ir.expressions.IrFunctionAccessExpression
import org.jetbrains.org.objectweb.asm.Label
import kotlin.math.exp
object Not : IntrinsicMethod() {
class BooleanNegation(val value: BooleanValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) = value.jumpIfTrue(target)
override fun jumpIfTrue(target: Label) = value.jumpIfFalse(target)
class BooleanNegation(val expression: IrFunctionAccessExpression, val value: BooleanValue) : BooleanValue(value.codegen) {
override fun jumpIfFalse(target: Label) {
markLineNumber(expression)
value.jumpIfTrue(target)
}
override fun jumpIfTrue(target: Label) {
markLineNumber(expression)
value.jumpIfFalse(target)
}
override fun discard() {
markLineNumber(expression)
value.discard()
}
}
override fun invoke(expression: IrFunctionAccessExpression, codegen: ExpressionCodegen, data: BlockInfo) =
BooleanNegation(expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean())
BooleanNegation(expression, expression.dispatchReceiver!!.accept(codegen, data).coerceToBoolean())
}
@@ -14,25 +14,37 @@ import org.jetbrains.org.objectweb.asm.Label
object OrOr : IntrinsicMethod() {
private class BooleanDisjunction(
val arg0: IrExpression, val arg1: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo
val left: IrExpression, val right: IrExpression, codegen: ExpressionCodegen, val data: BlockInfo
) : BooleanValue(codegen) {
override fun jumpIfFalse(target: Label) {
val stayLabel = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(stayLabel)
arg1.accept(codegen, data).coerceToBoolean().jumpIfFalse(target)
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfTrue(stayLabel)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfFalse(target)
mv.visitLabel(stayLabel)
}
override fun jumpIfTrue(target: Label) {
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
arg1.accept(codegen, data).coerceToBoolean().jumpIfTrue(target)
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfTrue(target)
val rightValue = right.accept(codegen, data).coerceToBoolean()
markLineNumber(right)
rightValue.jumpIfTrue(target)
}
override fun discard() {
val end = Label()
arg0.accept(codegen, data).coerceToBoolean().jumpIfTrue(end)
arg1.accept(codegen, data).discard()
val leftValue = left.accept(codegen, data).coerceToBoolean()
markLineNumber(left)
leftValue.jumpIfTrue(end)
val rightValue = right.accept(codegen, data)
markLineNumber(right)
rightValue.discard()
mv.visitLabel(end)
}
}