diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt index fbce9478855..f1ac72e789a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformerMethodVisitor.kt @@ -647,7 +647,7 @@ class CoroutineTransformerMethodVisitor( } for ((index, basicValue) in variablesToSpill) { - if (basicValue.type == NULL_TYPE) { + if (basicValue == StrictBasicValue.NULL_VALUE) { postponedActions.add { with(instructions) { insert(suspension.tryCatchBlockEndLabelAfterSuspensionCall, withInstructionAdapter { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt index 4b874723f4b..eab4bea44fd 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/SpilledVariableFieldTypesAnalysis.kt @@ -5,365 +5,156 @@ package org.jetbrains.kotlin.codegen.coroutines -import org.jetbrains.kotlin.codegen.inline.insnOpcodeText -import org.jetbrains.kotlin.codegen.inline.isFakeLocalVariableForInline +import org.jetbrains.kotlin.codegen.StackValue import org.jetbrains.kotlin.codegen.optimization.common.MethodAnalyzer -import org.jetbrains.kotlin.resolve.jvm.AsmTypes -import org.jetbrains.org.objectweb.asm.Opcodes.* +import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter +import org.jetbrains.org.objectweb.asm.Label +import org.jetbrains.org.objectweb.asm.Opcodes import org.jetbrains.org.objectweb.asm.Type +import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter import org.jetbrains.org.objectweb.asm.tree.* import org.jetbrains.org.objectweb.asm.tree.analysis.BasicValue import org.jetbrains.org.objectweb.asm.tree.analysis.Frame -import org.jetbrains.org.objectweb.asm.tree.analysis.Interpreter -import org.jetbrains.org.objectweb.asm.tree.analysis.Value // BasicValue interpreter from ASM does not distinct 'int' types from other int-like types like 'byte' or 'boolean', // neither do HotSpot and JVM spec. // But it seems like Dalvik does not follow it, and spilling boolean value into an 'int' field fails with VerifyError on Android 4, // so this function calculates refined frames' markup. // Note that type of some values is only possible to determine by their usages (e.g. ICONST_1, BALOAD both may push boolean or byte on stack) -// In this case, update the type of the value. +// In this case, coerce the type of the value. -// StrictBasicValue with mutable type -internal open class SpilledVariableFieldTypeValue(open var type: Type?, val insn: AbstractInsnNode?) : Value { - override fun getSize(): Int = type?.size ?: 1 +internal class IloadedValue(val insns: Set) : BasicValue(Type.INT_TYPE) - override fun equals(other: Any?): Boolean = other is SpilledVariableFieldTypeValue && type == other.type && insn == other.insn +private class IntLikeCoerceInterpreter : OptimizationBasicInterpreter() { + val needsToBeCoerced = mutableMapOf() - override fun hashCode(): Int = (type?.hashCode() ?: 0) xor insn.hashCode() + private fun coerce(value: IloadedValue, type: Type) { + for (insn in value.insns) { + needsToBeCoerced[insn] = type + } + } - override fun toString() = if (type == null) "." else "$type" -} - -private class MergedSpilledVariableFieldTypeValue( - val values: Set -) : SpilledVariableFieldTypeValue(null, null) { - override var type: Type? - get() = values.first().type - set(newType) { - for (value in values) { - value.type = newType - } + override fun copyOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? = + when { + insn.opcode == Opcodes.ILOAD -> IloadedValue(setOf(insn as VarInsnNode)) + value == null -> null + else -> BasicValue(value.type) } - override fun equals(other: Any?): Boolean = other is MergedSpilledVariableFieldTypeValue && other.values == values - - override fun hashCode(): Int = values.hashCode() - - override fun toString(): String = "M$values" -} - -// $i$a and $i$f variables should be ignored in merge operation, since they are not used, but set only -private class FakeInlinerVariableValue(insn: AbstractInsnNode) : SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) { - override fun toString(): String = "@" -} - -private operator fun SpilledVariableFieldTypeValue?.plus(other: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? = when { - this == null -> other - other == null -> this - this == other -> this - this is MergedSpilledVariableFieldTypeValue -> { - if (other is MergedSpilledVariableFieldTypeValue) MergedSpilledVariableFieldTypeValue(values + other.values) - else MergedSpilledVariableFieldTypeValue(values + other) + override fun binaryOperation(insn: AbstractInsnNode, v: BasicValue, w: BasicValue): BasicValue? { + if (insn.opcode == Opcodes.PUTFIELD) { + val expectedType = Type.getType((insn as FieldInsnNode).desc) + if (w is IloadedValue && expectedType.isIntLike()) { + coerce(w, expectedType) + } + } + return super.binaryOperation(insn, v, w) } - other is MergedSpilledVariableFieldTypeValue -> MergedSpilledVariableFieldTypeValue(other.values + this) - else -> MergedSpilledVariableFieldTypeValue(setOf(this, other)) -} -internal val NULL_TYPE = Type.getObjectType("null") + override fun unaryOperation(insn: AbstractInsnNode, value: BasicValue?): BasicValue? { + if (insn.opcode == Opcodes.PUTSTATIC) { + val expectedType = Type.getType((insn as FieldInsnNode).desc) + if (value is IloadedValue && expectedType.isIntLike()) { + coerce(value, expectedType) + } + } + return super.unaryOperation(insn, value) + } -// Same as BasicInterpreter, but updates types based on usages -private class SpilledVariableFieldTypesInterpreter( - private val methodNode: MethodNode -) : Interpreter(API_VERSION) { - override fun newValue(type: Type?): SpilledVariableFieldTypeValue? = - if (type == Type.VOID_TYPE) null else SpilledVariableFieldTypeValue(type, null) - - // INVOKEVIRTUAL, INVOKESPECIAL, INVOKESTATIC, INVOKEINTERFACE, - // MULTIANEWARRAY and INVOKEDYNAMIC - override fun naryOperation( - insn: AbstractInsnNode, - values: MutableList - ): SpilledVariableFieldTypeValue? { - fun updateTypes(argTypes: Array, withReceiver: Boolean) { + override fun naryOperation(insn: AbstractInsnNode, values: MutableList): BasicValue? { + fun checkTypes(argTypes: Array, withReceiver: Boolean) { val offset = if (withReceiver) 1 else 0 for ((index, argType) in argTypes.withIndex()) { val value = values[index + offset] ?: continue - if (argType.isIntType()) { - value.type = argType - } else if ( - (value.type == AsmTypes.OBJECT_TYPE && argType != AsmTypes.OBJECT_TYPE) || - value.type == NULL_TYPE || value.type == null - ) { - value.type = argType + if (argType.isIntLike() && value is IloadedValue) { + coerce(value, argType) } } } - - return SpilledVariableFieldTypeValue( - when (insn.opcode) { - MULTIANEWARRAY -> { - Type.getType((insn as MultiANewArrayInsnNode).desc) - } - INVOKEDYNAMIC -> { - updateTypes(Type.getArgumentTypes((insn as InvokeDynamicInsnNode).desc), false) - Type.getReturnType(insn.desc) - } - INVOKESTATIC -> { - updateTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), false) - Type.getReturnType(insn.desc) - } - INVOKEVIRTUAL, INVOKEINTERFACE, INVOKESPECIAL -> { - updateTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), true) - Type.getReturnType(insn.desc) - } - else -> { - unreachable(insn) - } - }, insn - ) - } - - private fun Type.isIntType(): Boolean = when (sort) { - Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT, Type.INT -> true - else -> false - } - - private fun unreachable(insn: AbstractInsnNode): Nothing = error("Unreachable instruction ${insn.insnOpcodeText}") - - // IASTORE, LASTORE, FASTORE, DASTORE, AASTORE, BASTORE, CASTORE, SASTORE - override fun ternaryOperation( - insn: AbstractInsnNode, - arrayref: SpilledVariableFieldTypeValue?, - index: SpilledVariableFieldTypeValue?, - value: SpilledVariableFieldTypeValue? - ): SpilledVariableFieldTypeValue? { when (insn.opcode) { - IASTORE, LASTORE, FASTORE, DASTORE, AASTORE -> { - // nothing to do + Opcodes.INVOKEDYNAMIC -> { + checkTypes(Type.getArgumentTypes((insn as InvokeDynamicInsnNode).desc), false) } - BASTORE -> { - value?.type = if (arrayref?.type?.descriptor == "[Z") Type.BOOLEAN_TYPE else Type.BYTE_TYPE + Opcodes.INVOKESTATIC -> { + checkTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), false) } - CASTORE -> { - value?.type = Type.CHAR_TYPE + Opcodes.INVOKEVIRTUAL, Opcodes.INVOKEINTERFACE, Opcodes.INVOKESPECIAL -> { + checkTypes(Type.getArgumentTypes((insn as MethodInsnNode).desc), true) } - SASTORE -> { - value?.type = Type.SHORT_TYPE - } - else -> unreachable(insn) } - return null + return super.naryOperation(insn, values) } - override fun merge(v: SpilledVariableFieldTypeValue?, w: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? = + override fun ternaryOperation(insn: AbstractInsnNode, arrayref: BasicValue?, index: BasicValue?, value: BasicValue?): BasicValue? { + when (insn.opcode) { + Opcodes.BASTORE -> { + if (value is IloadedValue) { + val type = if (arrayref?.type?.descriptor == "[Z") Type.BOOLEAN_TYPE else Type.BYTE_TYPE + coerce(value, type) + } + } + Opcodes.CASTORE -> { + if (value is IloadedValue) { + coerce(value, Type.CHAR_TYPE) + } + } + Opcodes.SASTORE -> { + if (value is IloadedValue) { + coerce(value, Type.SHORT_TYPE) + } + } + } + return super.ternaryOperation(insn, arrayref, index, value) + } + + override fun merge(v: BasicValue, w: BasicValue): BasicValue = when { - v is FakeInlinerVariableValue -> w - w is FakeInlinerVariableValue -> v - v?.type?.isIntType() == true && w?.type?.isIntType() == true -> v + w - v != null && v.type == null -> w - w != null && w.type == null -> v - v?.type == w?.type -> v - v?.type?.sort == Type.OBJECT && w?.type?.sort == Type.OBJECT -> { - when { - v.type == AsmTypes.OBJECT_TYPE -> v - w.type == AsmTypes.OBJECT_TYPE -> w - else -> SpilledVariableFieldTypeValue(AsmTypes.OBJECT_TYPE, v.insn) + v is IloadedValue && w is IloadedValue && v.type == w.type -> { + val insns = v.insns + w.insns + insns.find { it in needsToBeCoerced }?.let { + val type = needsToBeCoerced[it]!! + coerce(v, type) + coerce(w, type) } + IloadedValue(insns) } - else -> SpilledVariableFieldTypeValue(null, v?.insn ?: w?.insn) + v.type == w.type -> { + if (w is IloadedValue) w else v + } + else -> super.merge(v, w) } - - // IRETURN, LRETURN, FRETURN, DRETURN, ARETURN - override fun returnOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?, expected: SpilledVariableFieldTypeValue?) { - if (insn.opcode == IRETURN) { - value?.type = expected?.type - } - } - - // INEG, LNEG, FNEG, DNEG, IINC, I2L, I2F, I2D, L2I, L2F, L2D, F2I, F2L, - // F2D, D2I, D2L, D2F, I2B, I2C, I2S, IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, - // TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, - // PUTSTATIC, GETFIELD, NEWARRAY, ANEWARRAY, ARRAYLENGTH, ATHROW, CHECKCAST, - // INSTANCEOF, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL - override fun unaryOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? = - when (insn.opcode) { - INEG, LNEG, FNEG, DNEG, IINC -> SpilledVariableFieldTypeValue(value?.type, insn) - I2L, F2L, D2L -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn) - I2F, L2F, D2F -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn) - L2D, I2D, F2D -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn) - L2I, F2I, D2I, ARRAYLENGTH -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - I2B -> SpilledVariableFieldTypeValue(Type.BYTE_TYPE, insn) - I2C -> SpilledVariableFieldTypeValue(Type.CHAR_TYPE, insn) - I2S -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn) - IFEQ, IFNE, IFLT, IFGE, IFGT, IFLE, TABLESWITCH, LOOKUPSWITCH, IRETURN, LRETURN, FRETURN, DRETURN, ARETURN, - ATHROW, MONITORENTER, MONITOREXIT, IFNULL, IFNONNULL -> null - PUTSTATIC -> { - val expectedType = Type.getType((insn as FieldInsnNode).desc) - if (expectedType.isIntType()) { - value?.type = expectedType - } - null - } - GETFIELD -> SpilledVariableFieldTypeValue(Type.getType((insn as FieldInsnNode).desc), insn) - NEWARRAY -> when ((insn as IntInsnNode).operand) { - T_BOOLEAN -> SpilledVariableFieldTypeValue(Type.getType("[Z"), insn) - T_CHAR -> SpilledVariableFieldTypeValue(Type.getType("[C"), insn) - T_BYTE -> SpilledVariableFieldTypeValue(Type.getType("[B"), insn) - T_SHORT -> SpilledVariableFieldTypeValue(Type.getType("[S"), insn) - T_INT -> SpilledVariableFieldTypeValue(Type.getType("[I"), insn) - T_FLOAT -> SpilledVariableFieldTypeValue(Type.getType("[F"), insn) - T_DOUBLE -> SpilledVariableFieldTypeValue(Type.getType("[D"), insn) - T_LONG -> SpilledVariableFieldTypeValue(Type.getType("[J"), insn) - else -> unreachable(insn) - } - ANEWARRAY -> SpilledVariableFieldTypeValue(Type.getType("[${Type.getObjectType((insn as TypeInsnNode).desc)}"), insn) - CHECKCAST -> SpilledVariableFieldTypeValue(Type.getObjectType((insn as TypeInsnNode).desc), insn) - INSTANCEOF -> SpilledVariableFieldTypeValue(Type.BOOLEAN_TYPE, insn) - else -> unreachable(insn) - } - - // IALOAD, LALOAD, FALOAD, DALOAD, AALOAD, BALOAD, CALOAD, SALOAD, IADD, - // LADD, FADD, DADD, ISUB, LSUB, FSUB, DSUB, IMUL, LMUL, FMUL, DMUL, IDIV, - // LDIV, FDIV, DDIV, IREM, LREM, FREM, DREM, ISHL, LSHL, ISHR, LSHR, IUSHR, - // LUSHR, IAND, LAND, IOR, LOR, IXOR, LXOR, LCMP, FCMPL, FCMPG, DCMPL, - // DCMPG, IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, - // IF_ACMPEQ, IF_ACMPNE, PUTFIELD - override fun binaryOperation( - insn: AbstractInsnNode, - v: SpilledVariableFieldTypeValue?, - w: SpilledVariableFieldTypeValue? - ): SpilledVariableFieldTypeValue? = when (insn.opcode) { - IALOAD, IADD, ISUB, IMUL, IDIV, IREM, ISHL, ISHR, IUSHR, IAND, IOR, IXOR, LCMP, FCMPL, FCMPG, DCMPL, - DCMPG -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - LALOAD, LADD, LSUB, LMUL, LDIV, LREM, LSHL, LSHR, LUSHR, LAND, LOR, LXOR -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn) - FALOAD, FADD, FSUB, FMUL, FDIV, FREM -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn) - DALOAD, DADD, DSUB, DMUL, DDIV, DREM -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn) - AALOAD -> SpilledVariableFieldTypeValue(AsmTypes.OBJECT_TYPE, insn) - BALOAD -> SpilledVariableFieldTypeValue(if (v?.type?.descriptor == "[Z") Type.BOOLEAN_TYPE else Type.BYTE_TYPE, insn) - CALOAD -> SpilledVariableFieldTypeValue(Type.CHAR_TYPE, insn) - SALOAD -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn) - IF_ICMPEQ, IF_ICMPNE, IF_ICMPLT, IF_ICMPGE, IF_ICMPGT, IF_ICMPLE, IF_ACMPEQ, IF_ACMPNE -> null - PUTFIELD -> { - val expectedType = Type.getType((insn as FieldInsnNode).desc) - if (expectedType.isIntType()) { - w?.type = expectedType - } - null - } - else -> unreachable(insn) - } - - // ILOAD, LLOAD, FLOAD, DLOAD, ALOAD, ISTORE, LSTORE, FSTORE, DSTORE, - // ASTORE, DUP, DUP_X1, DUP_X2, DUP2, DUP2_X1, DUP2_X2, SWAP - override fun copyOperation(insn: AbstractInsnNode, value: SpilledVariableFieldTypeValue?): SpilledVariableFieldTypeValue? { - return when (insn.opcode) { - // If same ICONST is stored into several slots, thay can have different types - // For example, - // val b: Byte = 1 - // val i: Int = b.toInt() - // In this case, `b` and `i` have the same source, but different types. - // The example also shows, that the types should be `I`. - ISTORE -> { - if (value is FakeInlinerVariableValue && insn.previous.opcode == ICONST_0) return value - findLvtRecord((insn as VarInsnNode).`var`, insn)?.let { - if (Type.getType(it.desc) == value?.type) return value - } - var current: AbstractInsnNode? = insn - while (current != null && current !is LabelNode) { - current = current.next - } - while (current is LabelNode) { - findLvtRecord(insn.`var`, current)?.let { - if (Type.getType(it.desc) == value?.type) return value - } - current = current.next - } - if (value?.type == Type.INT_TYPE) return value - SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - } - // Sometimes we cannot get the type from the usage only - // For example, - // val c = '1' - // if (c == '2) ... - // In this case, update the type using information from LVT - ILOAD -> { - if (value is FakeInlinerVariableValue) return SpilledVariableFieldTypeValue(value.type, value.insn) - findLvtRecord((insn as VarInsnNode).`var`, insn)?.let { value?.type = Type.getType(it.desc) } - value - } - else -> value - } - } - - private fun findLvtRecord(index: Int, insn: AbstractInsnNode): LocalVariableNode? = methodNode.localVariables.find { local -> - local.index == index && - methodNode.instructions.indexOf(local.start) <= methodNode.instructions.indexOf(insn) && - methodNode.instructions.indexOf(insn) < methodNode.instructions.indexOf(local.end) - } - - // ACONST_NULL, ICONST_M1, ICONST_0, ICONST_1, ICONST_2, ICONST_3, ICONST_4, - // ICONST_5, LCONST_0, LCONST_1, FCONST_0, FCONST_1, FCONST_2, DCONST_0, - // DCONST_1, BIPUSH, SIPUSH, LDC, JSR, GETSTATIC, NEW - override fun newOperation(insn: AbstractInsnNode): SpilledVariableFieldTypeValue? { - return when (insn.opcode) { - ICONST_0 -> { - if (insn.next?.opcode == ISTORE) { - // Find out, whether this is fake inliner variable - // Unlike old JVM BE, JVM_IR does not generate them in a specific pattern, - // that we can just recognize. - // So, run instructions until LabelNode and check whether this is, in fact, fake inliner variable - if (findLvtRecord((insn.next as VarInsnNode).`var`, insn.next) != null) { - return SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - } - var current: AbstractInsnNode? = insn.next?.next - while (current != null && current !is LabelNode) { - current = current.next - } - while (current is LabelNode) { - val lvtRecord = findLvtRecord((insn.next as VarInsnNode).`var`, current) - // fake variables do not have LVT entry is they are inside `run` or `apply` - if (lvtRecord == null || isFakeLocalVariableForInline(lvtRecord.name)) { - return FakeInlinerVariableValue(insn) - } - current = current.next - } - } - SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - } - ACONST_NULL -> SpilledVariableFieldTypeValue(NULL_TYPE, insn) - ICONST_M1, ICONST_1, ICONST_2, ICONST_3, ICONST_4, ICONST_5 -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - LCONST_0, LCONST_1 -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn) - FCONST_0, FCONST_1, FCONST_2 -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn) - DCONST_0, DCONST_1 -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn) - BIPUSH -> SpilledVariableFieldTypeValue(Type.BYTE_TYPE, insn) - SIPUSH -> SpilledVariableFieldTypeValue(Type.SHORT_TYPE, insn) - LDC -> when ((insn as LdcInsnNode).cst) { - is Int -> SpilledVariableFieldTypeValue(Type.INT_TYPE, insn) - is Long -> SpilledVariableFieldTypeValue(Type.LONG_TYPE, insn) - is Float -> SpilledVariableFieldTypeValue(Type.FLOAT_TYPE, insn) - is Double -> SpilledVariableFieldTypeValue(Type.DOUBLE_TYPE, insn) - is String -> SpilledVariableFieldTypeValue(AsmTypes.JAVA_STRING_TYPE, insn) - is Type -> SpilledVariableFieldTypeValue(AsmTypes.JAVA_CLASS_TYPE, insn) - else -> SpilledVariableFieldTypeValue(AsmTypes.OBJECT_TYPE, insn) - } - JSR -> SpilledVariableFieldTypeValue(Type.VOID_TYPE, insn) - GETSTATIC -> SpilledVariableFieldTypeValue(Type.getType((insn as FieldInsnNode).desc), insn) - NEW -> SpilledVariableFieldTypeValue(Type.getObjectType((insn as TypeInsnNode).desc), insn) - else -> unreachable(insn) - } - } } internal fun performSpilledVariableFieldTypesAnalysis( methodNode: MethodNode, thisName: String -): Array?> = - MethodAnalyzer(thisName, methodNode, SpilledVariableFieldTypesInterpreter(methodNode)).analyze() +): Array?> { + val interpreter = IntLikeCoerceInterpreter() + MethodAnalyzer(thisName, methodNode, interpreter).analyze() + for ((insn, type) in interpreter.needsToBeCoerced) { + methodNode.instructions.insert(insn, withInstructionAdapter { coerceInt(type, this) }) + } + return MethodAnalyzer(thisName, methodNode, OptimizationBasicInterpreter()).analyze() +} -internal val Value.type: Type? - get() = when (this) { - is BasicValue -> type - is SpilledVariableFieldTypeValue -> type - else -> error("Unexpected type of $this") - } \ No newline at end of file +private fun coerceInt(to: Type, v: InstructionAdapter) { + if (to == Type.BOOLEAN_TYPE) { + with(v) { + val zeroLabel = Label() + val resLabel = Label() + ifeq(zeroLabel) + iconst(1) + goTo(resLabel) + mark(zeroLabel) + iconst(0) + mark(resLabel) + } + } else { + StackValue.coerce(Type.INT_TYPE, to, v) + } +} + +private fun Type.isIntLike(): Boolean = when (sort) { + Type.BOOLEAN, Type.BYTE, Type.CHAR, Type.SHORT -> true + else -> false +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt index 39dea2b297c..3d08806ba6a 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt @@ -37,5 +37,5 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.Z\$0 : Z -// 1 PUTFIELD .*\.Z\$1 : Z +// 1 PUTFIELD .*\.I\$0 : I +// 1 PUTFIELD .*\.I\$1 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt index 660b8be959b..4e030edf190 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt @@ -36,4 +36,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B +// 1 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt index c95e3a622ac..cee5926a9dc 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt @@ -35,4 +35,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.Z\$0 : Z +// 1 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt index 2f69c82f5b4..7fb114d3c2b 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt @@ -35,4 +35,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B +// 1 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt index 969add6338d..2e82a2c91df 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt @@ -35,4 +35,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.Z\$0 : Z +// 1 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt index dab606d0c2d..593b9b004de 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt @@ -38,5 +38,5 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B // 1 PUTFIELD .*\.I\$0 : I +// 1 PUTFIELD .*\.I\$1 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt index b51c9503d7c..8be753e320b 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt @@ -81,8 +81,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B -// 1 PUTFIELD .*\.C\$0 : C -// 1 PUTFIELD .*\.S\$0 : S -// 1 PUTFIELD .*\.Z\$0 : Z -// 1 PUTFIELD .*\.I\$0 : I +// 5 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt index 86ea32b9374..e12930d9dab 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt @@ -85,8 +85,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B -// 1 PUTFIELD .*\.C\$0 : C -// 1 PUTFIELD .*\.S\$0 : S -// 1 PUTFIELD .*\.Z\$0 : Z -// 1 PUTFIELD .*\.I\$0 : I +// 5 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt index c37f92f4306..4ad238b035b 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt @@ -71,8 +71,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B -// 1 PUTFIELD .*\.C\$0 : C -// 1 PUTFIELD .*\.S\$0 : S -// 1 PUTFIELD .*\.Z\$0 : Z -// 1 PUTFIELD .*\.I\$0 : I +// 5 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt index 28b35aeb946..d67c6a221e6 100644 --- a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt @@ -61,8 +61,4 @@ fun box(): String { return "OK" } -// 1 PUTFIELD .*\.B\$0 : B -// 1 PUTFIELD .*\.C\$0 : C -// 1 PUTFIELD .*\.S\$0 : S -// 1 PUTFIELD .*\.Z\$0 : Z -// 1 PUTFIELD .*\.I\$0 : I +// 5 PUTFIELD .*\.I\$0 : I