From 1df9724c0c13e33c8de540c1364dc92ae9472874 Mon Sep 17 00:00:00 2001 From: Denis Zharkov Date: Tue, 9 Aug 2016 11:53:43 +0300 Subject: [PATCH] Refine stack frames markup calculation It fixes VerifyError with coroutines on Dalvik happening because of variables spilling before/after suspension point BasicInterpreter 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 it's necessary to distinct int types for variables spilling #KT-13289 Fixed --- .../CoroutineTransformationClassBuilder.kt | 9 +- .../coroutines/refinedIntTypesAnalysis.kt | 232 ++++++++++++++++++ .../intLikeVarSpilling/complicatedMerge.kt | 29 +++ .../intLikeVarSpilling/i2bResult.kt | 28 +++ .../loadFromBooleanArray.kt | 27 ++ .../intLikeVarSpilling/loadFromByteArray.kt | 27 ++ .../intLikeVarSpilling/noVariableInTable.kt | 27 ++ .../intLikeVarSpilling/sameIconst1ManyVars.kt | 30 +++ .../intLikeVarSpilling/usedInArrayStore.kt | 74 ++++++ .../intLikeVarSpilling/usedInMethodCall.kt | 77 ++++++ .../intLikeVarSpilling/usedInPutfield.kt | 64 +++++ .../intLikeVarSpilling/usedInVarStore.kt | 53 ++++ .../intLikeVarSpilling/complicatedMerge.kt | 32 +++ .../intLikeVarSpilling/i2bResult.kt | 30 +++ .../loadFromBooleanArray.kt | 29 +++ .../intLikeVarSpilling/loadFromByteArray.kt | 29 +++ .../intLikeVarSpilling/noVariableInTable.kt | 29 +++ .../intLikeVarSpilling/sameIconst1ManyVars.kt | 33 +++ .../intLikeVarSpilling/usedInArrayStore.kt | 80 ++++++ .../intLikeVarSpilling/usedInMethodCall.kt | 83 +++++++ .../intLikeVarSpilling/usedInPutfield.kt | 70 ++++++ .../intLikeVarSpilling/usedInVarStore.kt | 59 +++++ .../codegen/BlackBoxCodegenTestGenerated.java | 69 ++++++ .../codegen/BytecodeTextTestGenerated.java | 69 ++++++ 24 files changed, 1284 insertions(+), 5 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/refinedIntTypesAnalysis.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt create mode 100644 compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt create mode 100644 compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt index 5bfe0ecd5d0..85ae871f1b2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/CoroutineTransformationClassBuilder.kt @@ -20,13 +20,12 @@ import com.intellij.util.containers.Stack import org.jetbrains.kotlin.codegen.* import org.jetbrains.kotlin.codegen.optimization.DeadCodeEliminationMethodTransformer import org.jetbrains.kotlin.codegen.optimization.FixStackWithLabelNormalizationMethodTransformer -import org.jetbrains.kotlin.codegen.optimization.common.OptimizationBasicInterpreter import org.jetbrains.kotlin.codegen.optimization.common.analyzeLiveness import org.jetbrains.kotlin.codegen.optimization.common.insnListOf import org.jetbrains.kotlin.codegen.optimization.common.removeEmptyCatchBlocks -import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer import org.jetbrains.kotlin.resolve.jvm.AsmTypes import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin +import org.jetbrains.kotlin.utils.sure import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.MethodVisitor import org.jetbrains.org.objectweb.asm.Opcodes @@ -191,7 +190,7 @@ class CoroutineTransformerMethodVisitor( private fun spillVariables(suspensionPoints: List, methodNode: MethodNode) { val instructions = methodNode.instructions - val frames = MethodTransformer.analyze(classBuilder.thisName, methodNode, OptimizationBasicInterpreter()) + val frames = performRefinedTypeAnalysis(methodNode, classBuilder.thisName) fun AbstractInsnNode.index() = instructions.indexOf(this) // We postpone these actions because they change instruction indices that we use when obtaining frames @@ -202,11 +201,11 @@ class CoroutineTransformerMethodVisitor( for (suspension in suspensionPoints) { val suspensionCallBegin = suspension.suspensionCallBegin val suspensionCallEnd = suspension.suspensionCallEnd - assert(frames[suspensionCallEnd.next.index()].stackSize == (if (suspension.returnType.sort == Type.VOID) 0 else 1)) { + assert(frames[suspensionCallEnd.next.index()]?.stackSize == (if (suspension.returnType.sort == Type.VOID) 0 else 1)) { "Stack should be spilled before suspension call" } - val frame = frames[suspensionCallBegin.index()] + val frame = frames[suspensionCallBegin.index()].sure { "Suspension points containing in dead code must be removed" } val localsCount = frame.locals val varsCountByType = mutableMapOf() diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/refinedIntTypesAnalysis.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/refinedIntTypesAnalysis.kt new file mode 100644 index 00000000000..4397a718b6e --- /dev/null +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/coroutines/refinedIntTypesAnalysis.kt @@ -0,0 +1,232 @@ +/* + * Copyright 2010-2016 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.codegen.coroutines + +import org.jetbrains.kotlin.codegen.optimization.common.* +import org.jetbrains.kotlin.codegen.optimization.fixStack.peek +import org.jetbrains.kotlin.codegen.optimization.fixStack.top +import org.jetbrains.kotlin.codegen.optimization.transformer.MethodTransformer +import org.jetbrains.org.objectweb.asm.Opcodes +import org.jetbrains.org.objectweb.asm.Type +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.SourceInterpreter +import org.jetbrains.org.objectweb.asm.tree.analysis.SourceValue +import java.util.* + +// 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) +internal fun performRefinedTypeAnalysis(methodNode: MethodNode, thisName: String): Array?> { + val insnList = methodNode.instructions + val basicFrames = MethodTransformer.analyze(thisName, methodNode, OptimizationBasicInterpreter()) + val sourceValueFrames = MethodTransformer.analyze(thisName, methodNode, MySourceInterpreter()) + + val expectedTypeAndSourcesByInsnIndex: Array>?> = arrayOfNulls(insnList.size()) + + fun AbstractInsnNode.index() = insnList.indexOf(this) + + fun saveExpectedType(value: SourceValue?, expectedType: Type) { + if (value == null) return + if (expectedType.sort !in REFINED_INT_SORTS) return + + value.insns.forEach { + val index = insnList.indexOf(it) + + checkUpdatedExpectedType(expectedTypeAndSourcesByInsnIndex[index]?.first, expectedType) + + expectedTypeAndSourcesByInsnIndex[index] = + Pair(expectedType, + expectedTypeAndSourcesByInsnIndex[index]?.second.orEmpty() + value) + } + } + + fun saveExpectedTypeForArrayStore(insn: AbstractInsnNode, sourceValueFrame: Frame) { + val arrayStoreType = + when (insn.opcode) { + Opcodes.BASTORE -> Type.BYTE_TYPE + Opcodes.CASTORE -> Type.CHAR_TYPE + Opcodes.SASTORE -> Type.SHORT_TYPE + else -> return + } + + val insnIndex = insnList.indexOf(insn) + + val arrayArg = basicFrames[insnIndex].peek(2) + // may be different from 'arrayStoreType' in case of boolean arrays (BASTORE opcode is also used for them) + val expectedType = + if (arrayArg?.type?.sort == Type.ARRAY) + arrayArg.type.elementType + else + arrayStoreType + + saveExpectedType(sourceValueFrame.top(), expectedType) + } + + fun saveExpectedTypeForFieldOrMethod(insn: AbstractInsnNode, sourceValueFrame: Frame) { + when (insn.opcode) { + Opcodes.PUTFIELD, Opcodes.PUTSTATIC -> + saveExpectedType(sourceValueFrame.top(), Type.getType((insn as FieldInsnNode).desc)) + + Opcodes.INVOKESTATIC, Opcodes.INVOKEVIRTUAL, Opcodes.INVOKEINTERFACE, Opcodes.INVOKESPECIAL -> { + val argumentTypes = Type.getArgumentTypes((insn as MethodInsnNode).desc) + argumentTypes.withIndex().forEach { + val (argIndex, type) = it + saveExpectedType(sourceValueFrame.peek(argumentTypes.size - argIndex - 1), type) + } + } + } + } + + fun saveExpectedTypeForVarStore(insn: AbstractInsnNode, sourceValueFrame: Frame) { + if (insn.isIntStore()) { + val varIndex = (insn as VarInsnNode).`var` + // Considering next insn is important because variable initializer is emitted just before + // the beginning of variable + val nextInsn = InsnSequence(insn.next, insnList.last).firstOrNull(AbstractInsnNode::isMeaningful) + + val variableNode = + methodNode.findContainingVariableFromTable(insn, varIndex) + ?: methodNode.findContainingVariableFromTable(nextInsn ?: return, varIndex) + ?: return + + saveExpectedType(sourceValueFrame.top(), Type.getType(variableNode.desc)) + } + } + + for ((insnIndex, insn) in insnList.toArray().withIndex()) { + assert(insn.opcode != Opcodes.IRETURN) { + "Coroutine body must not contain IRETURN instructions because 'doResume' is always void" + } + + val sourceValueFrame = sourceValueFrames[insnIndex] ?: continue + + saveExpectedTypeForArrayStore(insn, sourceValueFrame) + saveExpectedTypeForFieldOrMethod(insn, sourceValueFrame) + saveExpectedTypeForVarStore(insn, sourceValueFrame) + } + + val refinedVarFrames = analyze(methodNode, object : BackwardAnalysisInterpreter { + override fun newFrame(maxLocals: Int): VarExpectedTypeFrame = VarExpectedTypeFrame(maxLocals) + + override fun def(frame: VarExpectedTypeFrame, insn: AbstractInsnNode) { + if (insn.isIntStore()) { + frame.expectedTypeByVarIndex[(insn as VarInsnNode).`var`] = null + } + } + + override fun use(frame: VarExpectedTypeFrame, insn: AbstractInsnNode) { + val (expectedType, sources) = expectedTypeAndSourcesByInsnIndex[insn.index()] ?: return + + sources.flatMap(SourceValue::insns).forEach { + insn -> + if (insn.isIntLoad()) { + frame.updateExpectedType((insn as VarInsnNode).`var`, expectedType) + } + } + } + }) + + val refinedFrames = Array(basicFrames.size) { + insnIndex -> + val current = Frame(basicFrames[insnIndex] ?: return@Array null) + + refinedVarFrames[insnIndex].expectedTypeByVarIndex.withIndex().filter { it.value != null }.forEach { + assert(current.getLocal(it.index)?.type == Type.INT_TYPE) { + "int type expected, but ${current.getLocal(it.index)?.type} was found in basic frames" + } + + current.setLocal(it.index, BasicValue(it.value)) + } + + current + } + + return refinedFrames +} + +private fun AbstractInsnNode.isIntLoad() = opcode == Opcodes.ILOAD +private fun AbstractInsnNode.isIntStore() = opcode == Opcodes.ISTORE + +private fun checkUpdatedExpectedType(was: Type?, new: Type) { + assert(was == null || was == new) { + "Conflicting expected types: $was/$new" + } +} + +private class MySourceInterpreter : SourceInterpreter() { + override fun copyOperation(insn: AbstractInsnNode, value: SourceValue) = + when { + insn.isStoreOperation() || insn.isLoadOperation() -> SourceValue(value.size, insn) + // For DUP* instructions return the same value (effectively ignore DUP's) + else -> value + } +} + +private val REFINED_INT_SORTS = setOf(Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT) + +private fun MethodNode.findContainingVariableFromTable(insn: AbstractInsnNode, varIndex: Int): LocalVariableNode? { + val insnIndex = instructions.indexOf(insn) + return localVariables.firstOrNull { + it.index == varIndex && it.rangeContainsInsn(insnIndex, instructions) + } +} + +private fun LocalVariableNode.rangeContainsInsn(insnIndex: Int, insnList: InsnList) = + insnList.indexOf(start) < insnIndex && insnIndex < insnList.indexOf(end) + +private class VarExpectedTypeFrame(maxLocals: Int) : VarFrame { + val expectedTypeByVarIndex = arrayOfNulls(maxLocals) + + override fun mergeFrom(other: VarExpectedTypeFrame) { + assert(expectedTypeByVarIndex.size == other.expectedTypeByVarIndex.size) { + "Other VarExpectedTypeFrame has different size: ${expectedTypeByVarIndex.size} / ${other.expectedTypeByVarIndex.size}" + } + + for ((varIndex, type) in other.expectedTypeByVarIndex.withIndex()) { + updateExpectedType(varIndex, type ?: continue) + } + } + + fun updateExpectedType(varIndex: Int, new: Type) { + val was = expectedTypeByVarIndex[varIndex] + // Widening to int is always allowed + if (new == Type.INT_TYPE) return + + checkUpdatedExpectedType(was, new) + + expectedTypeByVarIndex[varIndex] = new + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other?.javaClass != javaClass) return false + + other as VarExpectedTypeFrame + + if (!Arrays.equals(expectedTypeByVarIndex, other.expectedTypeByVarIndex)) return false + + return true + } + + override fun hashCode(): Int { + return Arrays.hashCode(expectedTypeByVarIndex) + } +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt new file mode 100644 index 00000000000..8e8c9f5d9f1 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt @@ -0,0 +1,29 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun foo() = true + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +fun box(): String { + builder { + val x = true + val y = false + suspendHere() + setBooleanRes(if (foo()) x else y) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt new file mode 100644 index 00000000000..f4c86ef86fd --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt @@ -0,0 +1,28 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +fun foo(): Int = 1 + +fun box(): String { + builder { + val x: Byte = foo().toByte() + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt new file mode 100644 index 00000000000..f791030fd76 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt @@ -0,0 +1,27 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +fun box(): String { + builder { + val a = booleanArrayOf(true) + val x = a[0] + suspendHere() + setBooleanRes(x) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt new file mode 100644 index 00000000000..888b7c76543 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt @@ -0,0 +1,27 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +fun box(): String { + builder { + val a = byteArrayOf(1) + val x = a[0] + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt new file mode 100644 index 00000000000..2d4af13cabf --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt @@ -0,0 +1,27 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean, ignored: Unit) { + booleanResult = x +} + +fun box(): String { + builder { + // 'true' value is spilled into variable and saved to field before suspension point + // It's important that there is no type info about this variable in local var table, + // so we should infer that ICONST_1 is a boolean value from it's usage + setBooleanRes(true, suspendHere()) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt new file mode 100644 index 00000000000..4c10a589253 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt @@ -0,0 +1,30 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var result: String = "" +fun setRes(x: Byte, y: Int) { + result = "$x#$y" +} + +fun foo(): Int = 1 + +fun box(): String { + builder { + val x: Byte = 1 + // No actual cast happens here + val y: Int = x.toInt() + suspendHere() + setRes(x, y) + } + + if (result != "1#1") return "fail 1" + + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt new file mode 100644 index 00000000000..866998b14c7 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt @@ -0,0 +1,74 @@ +// WITH_RUNTIME +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +@JvmField +var booleanResult = booleanArrayOf() +@JvmField +var charResult = charArrayOf() +@JvmField +var byteResult = byteArrayOf() +@JvmField +var shortResult = shortArrayOf() +@JvmField +var intResult = intArrayOf() + +fun box(): String { + builder { + val x = true + suspendHere() + val a = BooleanArray(1) + a[0] = x + booleanResult = a + } + + if (!booleanResult[0]) return "fail 1" + + builder { + val x = '1' + suspendHere() + val a = CharArray(1) + a[0] = x + charResult = a + } + + if (charResult[0] != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + val a = ByteArray(1) + a[0] = x + byteResult = a + } + + if (byteResult[0] != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + val a = ShortArray(1) + a[0] = x + shortResult = a + } + + if (shortResult[0] != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + val a = IntArray(1) + a[0] = x + intResult = a + } + + if (intResult[0] != 1) return "fail 5" + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt new file mode 100644 index 00000000000..a9939ff9721 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt @@ -0,0 +1,77 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +private var charResult: Char = '0' +fun setCharRes(x: Char) { + charResult = x +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +private var shortResult: Short = 0 +fun setShortRes(x: Short) { + shortResult = x +} + +private var intResult: Int = 0 +fun setIntRes(x: Int) { + intResult = x +} + +fun box(): String { + builder { + val x = true + suspendHere() + setBooleanRes(x) + } + + if (!booleanResult) return "fail 1" + + builder { + val x = '1' + suspendHere() + setCharRes(x) + } + + if (charResult != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + setShortRes(x) + } + + if (shortResult != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + setIntRes(x) + } + + if (intResult != 1) return "fail 5" + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt new file mode 100644 index 00000000000..f3996b77b4e --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt @@ -0,0 +1,64 @@ +// WITH_RUNTIME +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +@JvmField +var booleanResult = false +@JvmField +var charResult: Char = '0' +@JvmField +var byteResult: Byte = 0 +@JvmField +var shortResult: Short = 0 +@JvmField +var intResult: Int = 0 + +fun box(): String { + builder { + val x = true + suspendHere() + booleanResult = x + } + + if (!booleanResult) return "fail 1" + + builder { + val x = '1' + suspendHere() + charResult = x + } + + if (charResult != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + byteResult = x + } + + if (byteResult != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + shortResult = x + } + + if (shortResult != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + intResult = x + } + + if (intResult != 1) return "fail 5" + return "OK" +} diff --git a/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt new file mode 100644 index 00000000000..2ddb6f1b790 --- /dev/null +++ b/compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt @@ -0,0 +1,53 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + builder { + val x = true + suspendHere() + val y: Boolean = x + if (!y) throw IllegalStateException("fail 1") + } + + builder { + val x = '1' + suspendHere() + + val y: Char = x + if (y != '1') throw IllegalStateException("fail 2") + } + + builder { + val x: Byte = 1 + suspendHere() + + val y: Byte = x + if (y != 1.toByte()) throw IllegalStateException("fail 3") + } + + builder { + val x: Short = 1 + + suspendHere() + + val y: Short = x + if (y != 1.toShort()) throw IllegalStateException("fail 4") + } + + builder { + val x: Int = 1 + suspendHere() + + val y: Int = x + if (y != 1) throw IllegalStateException("fail 5") + } + + return "OK" +} diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt new file mode 100644 index 00000000000..91c1e73f246 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt @@ -0,0 +1,32 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun foo() = true + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +fun box(): String { + builder { + val x = true + val y = false + suspendHere() + setBooleanRes(if (foo()) x else y) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.Z\$0 : Z +// 1 PUTFIELD .*\.Z\$1 : Z diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt new file mode 100644 index 00000000000..e418af58b4a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt @@ -0,0 +1,30 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +fun foo(): Int = 1 + +fun box(): String { + builder { + val x: Byte = foo().toByte() + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.B\$0 : B diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt new file mode 100644 index 00000000000..e355ea9b293 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt @@ -0,0 +1,29 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +fun box(): String { + builder { + val a = booleanArrayOf(true) + val x = a[0] + suspendHere() + setBooleanRes(x) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.Z\$0 : Z diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt new file mode 100644 index 00000000000..d6836ba66ff --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt @@ -0,0 +1,29 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +fun box(): String { + builder { + val a = byteArrayOf(1) + val x = a[0] + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.B\$0 : B diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt new file mode 100644 index 00000000000..521f6ba6b01 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt @@ -0,0 +1,29 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean, ignored: Unit) { + booleanResult = x +} + +fun box(): String { + builder { + // 'true' value is spilled into variable and saved to field before suspension point + // It's important that there is no type info about this variable in local var table, + // so we should infer that ICONST_1 is a boolean value from it's usage + setBooleanRes(true, suspendHere()) + } + + if (!booleanResult) return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.Z\$0 : Z diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt new file mode 100644 index 00000000000..6fe01877767 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt @@ -0,0 +1,33 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var result: String = "" +fun setRes(x: Byte, y: Int) { + result = "$x#$y" +} + +fun foo(): Int = 1 + +fun box(): String { + builder { + val x: Byte = 1 + // No actual cast happens here + val y: Int = x.toInt() + suspendHere() + setRes(x, y) + } + + if (result != "1#1") return "fail 1" + + return "OK" +} + +// 1 PUTFIELD .*\.B\$0 : B +// 1 PUTFIELD .*\.I\$0 : I diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt new file mode 100644 index 00000000000..69e628ff83c --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt @@ -0,0 +1,80 @@ +// WITH_RUNTIME +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +@JvmField +var booleanResult = booleanArrayOf() +@JvmField +var charResult = charArrayOf() +@JvmField +var byteResult = byteArrayOf() +@JvmField +var shortResult = shortArrayOf() +@JvmField +var intResult = intArrayOf() + +fun box(): String { + builder { + val x = true + suspendHere() + val a = BooleanArray(1) + a[0] = x + booleanResult = a + } + + if (!booleanResult[0]) return "fail 1" + + builder { + val x = '1' + suspendHere() + val a = CharArray(1) + a[0] = x + charResult = a + } + + if (charResult[0] != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + val a = ByteArray(1) + a[0] = x + byteResult = a + } + + if (byteResult[0] != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + val a = ShortArray(1) + a[0] = x + shortResult = a + } + + if (shortResult[0] != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + val a = IntArray(1) + a[0] = x + intResult = a + } + + if (intResult[0] != 1) return "fail 5" + 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 diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt new file mode 100644 index 00000000000..29d2540f1d9 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt @@ -0,0 +1,83 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +private var booleanResult = false +fun setBooleanRes(x: Boolean) { + booleanResult = x +} + +private var charResult: Char = '0' +fun setCharRes(x: Char) { + charResult = x +} + +private var byteResult: Byte = 0 +fun setByteRes(x: Byte) { + byteResult = x +} + +private var shortResult: Short = 0 +fun setShortRes(x: Short) { + shortResult = x +} + +private var intResult: Int = 0 +fun setIntRes(x: Int) { + intResult = x +} + +fun box(): String { + builder { + val x = true + suspendHere() + setBooleanRes(x) + } + + if (!booleanResult) return "fail 1" + + builder { + val x = '1' + suspendHere() + setCharRes(x) + } + + if (charResult != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + setByteRes(x) + } + + if (byteResult != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + setShortRes(x) + } + + if (shortResult != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + setIntRes(x) + } + + if (intResult != 1) return "fail 5" + 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 diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt new file mode 100644 index 00000000000..e2701da4815 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt @@ -0,0 +1,70 @@ +// WITH_RUNTIME +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +@JvmField +var booleanResult = false +@JvmField +var charResult: Char = '0' +@JvmField +var byteResult: Byte = 0 +@JvmField +var shortResult: Short = 0 +@JvmField +var intResult: Int = 0 + +fun box(): String { + builder { + val x = true + suspendHere() + booleanResult = x + } + + if (!booleanResult) return "fail 1" + + builder { + val x = '1' + suspendHere() + charResult = x + } + + if (charResult != '1') return "fail 2" + + builder { + val x: Byte = 1 + suspendHere() + byteResult = x + } + + if (byteResult != 1.toByte()) return "fail 3" + + builder { + val x: Short = 1 + suspendHere() + shortResult = x + } + + if (shortResult != 1.toShort()) return "fail 4" + + builder { + val x: Int = 1 + suspendHere() + intResult = x + } + + if (intResult != 1) return "fail 5" + 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 diff --git a/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt new file mode 100644 index 00000000000..855669e9e63 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt @@ -0,0 +1,59 @@ +class Controller { + suspend fun suspendHere(x: Continuation) { + x.resume(Unit) + } +} + +fun builder(coroutine c: Controller.() -> Continuation) { + c(Controller()).resume(Unit) +} + +fun box(): String { + builder { + val x = true + suspendHere() + val y: Boolean = x + if (!y) throw IllegalStateException("fail 1") + } + + builder { + val x = '1' + suspendHere() + + val y: Char = x + if (y != '1') throw IllegalStateException("fail 2") + } + + builder { + val x: Byte = 1 + suspendHere() + + val y: Byte = x + if (y != 1.toByte()) throw IllegalStateException("fail 3") + } + + builder { + val x: Short = 1 + + suspendHere() + + val y: Short = x + if (y != 1.toShort()) throw IllegalStateException("fail 4") + } + + builder { + val x: Int = 1 + suspendHere() + + val y: Int = x + if (y != 1) throw IllegalStateException("fail 5") + } + + 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 diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 915acee0701..0410c7dff26 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -4557,6 +4557,75 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/varValueConflictsWithTableSameSort.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IntLikeVarSpilling extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInIntLikeVarSpilling() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/coroutines/intLikeVarSpilling"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("complicatedMerge.kt") + public void testComplicatedMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/complicatedMerge.kt"); + doTest(fileName); + } + + @TestMetadata("i2bResult.kt") + public void testI2bResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/i2bResult.kt"); + doTest(fileName); + } + + @TestMetadata("loadFromBooleanArray.kt") + public void testLoadFromBooleanArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt"); + doTest(fileName); + } + + @TestMetadata("loadFromByteArray.kt") + public void testLoadFromByteArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/loadFromByteArray.kt"); + doTest(fileName); + } + + @TestMetadata("noVariableInTable.kt") + public void testNoVariableInTable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/noVariableInTable.kt"); + doTest(fileName); + } + + @TestMetadata("sameIconst1ManyVars.kt") + public void testSameIconst1ManyVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt"); + doTest(fileName); + } + + @TestMetadata("usedInArrayStore.kt") + public void testUsedInArrayStore() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInArrayStore.kt"); + doTest(fileName); + } + + @TestMetadata("usedInMethodCall.kt") + public void testUsedInMethodCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInMethodCall.kt"); + doTest(fileName); + } + + @TestMetadata("usedInPutfield.kt") + public void testUsedInPutfield() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInPutfield.kt"); + doTest(fileName); + } + + @TestMetadata("usedInVarStore.kt") + public void testUsedInVarStore() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/coroutines/intLikeVarSpilling/usedInVarStore.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/box/dataClasses") diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index 8e96e9d7543..1f82cdc3d64 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -792,6 +792,75 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/varValueConflictsWithTableSameSort.kt"); doTest(fileName); } + + @TestMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class IntLikeVarSpilling extends AbstractBytecodeTextTest { + public void testAllFilesPresentInIntLikeVarSpilling() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("complicatedMerge.kt") + public void testComplicatedMerge() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/complicatedMerge.kt"); + doTest(fileName); + } + + @TestMetadata("i2bResult.kt") + public void testI2bResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/i2bResult.kt"); + doTest(fileName); + } + + @TestMetadata("loadFromBooleanArray.kt") + public void testLoadFromBooleanArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromBooleanArray.kt"); + doTest(fileName); + } + + @TestMetadata("loadFromByteArray.kt") + public void testLoadFromByteArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/loadFromByteArray.kt"); + doTest(fileName); + } + + @TestMetadata("noVariableInTable.kt") + public void testNoVariableInTable() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/noVariableInTable.kt"); + doTest(fileName); + } + + @TestMetadata("sameIconst1ManyVars.kt") + public void testSameIconst1ManyVars() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/sameIconst1ManyVars.kt"); + doTest(fileName); + } + + @TestMetadata("usedInArrayStore.kt") + public void testUsedInArrayStore() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInArrayStore.kt"); + doTest(fileName); + } + + @TestMetadata("usedInMethodCall.kt") + public void testUsedInMethodCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInMethodCall.kt"); + doTest(fileName); + } + + @TestMetadata("usedInPutfield.kt") + public void testUsedInPutfield() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInPutfield.kt"); + doTest(fileName); + } + + @TestMetadata("usedInVarStore.kt") + public void testUsedInVarStore() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/bytecodeText/coroutines/intLikeVarSpilling/usedInVarStore.kt"); + doTest(fileName); + } + } } @TestMetadata("compiler/testData/codegen/bytecodeText/deadCodeElimination")