From 697c8637ee7092c62d21575a199c0260866ab308 Mon Sep 17 00:00:00 2001 From: Ilmir Usmanov Date: Tue, 30 Jun 2020 14:03:51 +0200 Subject: [PATCH] For all int-like typed variables, use int as field type and coerce it during spill-unspill. Coerce int to boolean, otherwise, VerifyError is thrown on android Completely rewrite SpilledVariableFieldTypesAnalysis... again, but this time use BasicInterpreter This way, the analysis both does not use SourceInterpreter and is in line with the rest on analyses. --- .../CoroutineTransformerMethodVisitor.kt | 2 +- .../SpilledVariableFieldTypesAnalysis.kt | 429 +++++------------- .../intLikeVarSpilling/complicatedMerge.kt | 4 +- .../intLikeVarSpilling/i2bResult.kt | 2 +- .../loadFromBooleanArray.kt | 2 +- .../intLikeVarSpilling/loadFromByteArray.kt | 2 +- .../intLikeVarSpilling/noVariableInTable.kt | 2 +- .../intLikeVarSpilling/sameIconst1ManyVars.kt | 2 +- .../intLikeVarSpilling/usedInArrayStore.kt | 6 +- .../intLikeVarSpilling/usedInMethodCall.kt | 6 +- .../intLikeVarSpilling/usedInPutfield.kt | 6 +- .../intLikeVarSpilling/usedInVarStore.kt | 6 +- 12 files changed, 122 insertions(+), 347 deletions(-) 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