From 7680e7fd563748d57c9622f9a550ec011ac4d7bd Mon Sep 17 00:00:00 2001 From: Mark Punzalan Date: Fri, 29 Mar 2019 10:33:30 -0700 Subject: [PATCH] Use while loop for progressions that cannot overflow (instead of do-while with enclosing "not empty" check). Also do not add additional "not empty" condition for `until` loops when the given bound is a constant != MIN_VALUE. --- .../common/lower/loops/ForLoopsLowering.kt | 74 ++++++++++--------- .../backend/common/lower/loops/HeaderInfo.kt | 37 +++++++++- .../common/lower/loops/HeaderProcessor.kt | 42 +++++++---- .../common/lower/loops/ProgressionHandlers.kt | 61 ++++++++++----- .../forLoop/forInDownToCharMinValue.kt | 14 ++++ .../forLoop/forInDownToIntMinValue.kt | 14 ++++ .../forLoop/forInDownToLongMinValue.kt | 15 ++++ .../forInIndices/forInObjectArrayIndices.kt | 5 +- .../forInPrimitiveArrayIndices.kt | 5 +- .../forLoop/forInRangeToCharConst.kt | 10 ++- .../forLoop/forInRangeToCharMaxValue.kt | 14 ++++ .../bytecodeText/forLoop/forInRangeToConst.kt | 10 ++- .../forLoop/forInRangeToIntMaxValue.kt | 14 ++++ .../forLoop/forInRangeToLongConst.kt | 11 ++- .../forLoop/forInRangeToLongMaxValue.kt | 15 ++++ .../forLoop/forInRangeToQualifiedConst.kt | 10 ++- .../bytecodeText/forLoop/forInUntil.kt | 3 +- .../forLoop/forInUntilCharMaxValue.kt | 14 ++++ .../forLoop/forInUntilIntMaxValue.kt | 14 ++++ .../forLoop/forInUntilLongMaxValue.kt | 15 ++++ .../bytecodeText/forLoop/forIntInDownTo.kt | 4 +- .../forLoop/primitiveLiteralRange1.kt | 3 +- .../forLoop/primitiveLiteralRange2.kt | 3 +- .../codegen/BytecodeTextTestGenerated.java | 45 +++++++++++ .../ir/IrBytecodeTextTestGenerated.java | 45 +++++++++++ 25 files changed, 403 insertions(+), 94 deletions(-) create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt create mode 100644 compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt index a2442fe14ab..43c956facff 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ForLoopsLowering.kt @@ -23,6 +23,7 @@ import org.jetbrains.kotlin.ir.symbols.IrVariableSymbol import org.jetbrains.kotlin.ir.types.getClass import org.jetbrains.kotlin.ir.types.toKotlinType import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.patchDeclarationParents import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid @@ -39,41 +40,50 @@ val forLoopsPhase = makeIrFilePhase( * a simple while loop over primitive induction variable. * * For example, this loop: - * * ``` - * for (i in first..last step foo) { ... } + * for (i in first..last) { // Do something with i } * ``` - * * is represented in IR in such a manner: + * ``` + * val it = (first..last).iterator() + * while (it.hasNext()) { + * val i = it.next() + * // Do something with i + * } + * ``` + * We transform it into one of the following loops: * * ``` - * val it = (first..last step foo).iterator() - * while (it.hasNext()) { - * val i = it.next() - * ... - * } - * ``` + * // 1. If the induction variable cannot overflow, i.e., `last` is const and != MAX_VALUE (if increasing, or MIN_VALUE if decreasing), + * // OR if loop is an until loop (e.g., `for (i in first until last)`): * - * We transform it into the following loop: + * var inductionVar = first + * while (inductionVar <= last) { // (inductionVar >= last if the progression is decreasing) + * val i = inductionVar + * inductionVar++ + * // Do something with i + * } * - * ``` - * var it = first - * if (it <= last) { // (it >= last if the progression is decreasing) - * do { - * val i = it++ - * ... - * } while (i != last) - * } - * ``` - * - * In case of iteration over array we transform it into following: + * // 2. If the induction variable CAN overflow, i.e., `last` is not const or is MAX/MIN_VALUE: * + * var inductionVar = first + * if (inductionVar <= last) { // (inductionVar >= last if the progression is decreasing) + * // Loop is not empty + * do { + * val i = inductionVar + * inductionVar++ + * // Do something with i + * } while (i != last) + * } * ``` - * while (it <= array.size - 1) { - * val i = array[it] - * it++ - * ... - * } + * In case of iteration over an array, we transform it into the following: + * ``` + * var inductionVar = 0 + * val last = array.size - 1 + * while (inductionVar <= last) { + * val i = array[inductionVar++] + * // Do something with i + * } * ``` */ internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass { @@ -202,17 +212,13 @@ private class RangeLoopTransformer( // The "next" statement (at the top of the loop): // - // ``` - // val i = it.next() - // ``` + // val i = it.next() // // ...is lowered into: // - // ``` - // val i = initialValue() // `inductionVariable` for progressions - // // `array[inductionVariable]` for arrays - // inductionVariable = inductionVariable + step - // ``` + // val i = initializeLoopVariable() // `inductionVariable` for progressions + // // `array[inductionVariable]` for arrays + // inductionVariable = inductionVariable + step return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) { variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this) val plusFun = forLoopInfo.inductionVariable.type.getClass()!!.functions.first { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt index 3463e1db6fb..fd0b1418ee0 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderInfo.kt @@ -65,7 +65,7 @@ internal sealed class HeaderInfo( ) { val direction: ProgressionDirection by lazy { // If step is a constant (either Int or Long), then we can determine the direction. - val stepValue = (step as? IrConst<*>)?.value as? Number? + val stepValue = (step as? IrConst<*>)?.value as? Number val stepValueAsLong = stepValue?.toLong() when { stepValueAsLong == null -> ProgressionDirection.UNKNOWN @@ -82,9 +82,42 @@ internal class ProgressionHeaderInfo( first: IrExpression, last: IrExpression, step: IrExpression, + canOverflow: Boolean? = null, val additionalVariables: List = listOf(), val additionalNotEmptyCondition: IrExpression? = null -) : HeaderInfo(progressionType, first, last, step) +) : HeaderInfo(progressionType, first, last, step) { + + private val _canOverflow: Boolean? = canOverflow + val canOverflow: Boolean by lazy { + if (_canOverflow != null) return@lazy _canOverflow + + // Induction variable can overflow if it is not a const, or is MAX/MIN_VALUE (depending on direction). + val lastValue = (last as? IrConst<*>)?.value + val lastValueAsLong = when (lastValue) { + is Number -> lastValue.toLong() + is Char -> lastValue.toLong() + else -> return@lazy true // If "last" is not a const Number or Char. + } + val constLimitAsLong = when (direction) { + ProgressionDirection.UNKNOWN -> + // If we don't know the direction, we can't be sure which limit to use. + return@lazy true + ProgressionDirection.DECREASING -> + when (progressionType) { + ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong() + ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong() + ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE + } + ProgressionDirection.INCREASING -> + when (progressionType) { + ProgressionType.INT_PROGRESSION -> Int.MAX_VALUE.toLong() + ProgressionType.CHAR_PROGRESSION -> Char.MAX_VALUE.toLong() + ProgressionType.LONG_PROGRESSION -> Long.MAX_VALUE + } + } + constLimitAsLong == lastValueAsLong + } +} /** Information about a for-loop over an array. The internal induction variable used is an Int. */ internal class ArrayHeaderInfo( diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt index d4dcb9ee944..43d7ef15fbc 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/HeaderProcessor.kt @@ -66,24 +66,36 @@ internal class ProgressionLoopHeader( get() = headerInfo.additionalVariables + listOf(inductionVariable, last, step) override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) { - // Condition: loopVariable != last - assert(loopVariable != null) - val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" } - val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply { - putValueArgument(0, irGet(loopVariable!!)) - putValueArgument(1, irGet(last)) - }) - - - // TODO: Build while loop (instead of do-while) where possible, e.g., in "until" ranges, or when "last" is known to be < MAX_VALUE - IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { - label = loop.label - condition = newCondition - body = newBody + if (headerInfo.canOverflow) { + // Condition: loopVariable != last. We cannot use the induction variable because it can overflow. + assert(loopVariable != null) + val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" } + val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply { + putValueArgument(0, irGet(loopVariable!!)) + putValueArgument(1, irGet(last)) + }) + IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { + label = loop.label + condition = newCondition + body = newBody + } + } else { + // If the induction variable cannot overflow, use a while loop using the "not empty" condition. + val newCondition = buildNotEmptyCondition(this@with) + IrWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply { + label = loop.label + condition = newCondition + body = newBody + } } } + // If the induction variable cannot overflow, we do NOT need the enclosing "not empty" check because the for-loop is lowered into a + // while loop that uses the "not empty" condition (see buildInnerLoop()); the enclosing check would be redundant. override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? = + if (headerInfo.canOverflow) buildNotEmptyCondition(builder) else null + + private fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression = with(builder) { val builtIns = context.irBuiltIns val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType() @@ -158,7 +170,7 @@ internal class ArrayLoopHeader( get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step) override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) { - // Condition: loopVariable != last + // Condition: inductionVariable != last val builtIns = context.irBuiltIns val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]!! val newCondition = irCall(callee).apply { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt index d1c77065690..8c8daf6c330 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/backend/common/lower/loops/ProgressionHandlers.kt @@ -17,6 +17,7 @@ import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns import org.jetbrains.kotlin.ir.expressions.IrCall +import org.jetbrains.kotlin.ir.expressions.IrConst import org.jetbrains.kotlin.ir.expressions.IrExpression import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin import org.jetbrains.kotlin.ir.types.getClass @@ -79,42 +80,65 @@ internal class UntilHandler(private val context: CommonBackendContext, private v override fun build(call: IrCall, data: ProgressionType): HeaderInfo? = with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) { - // `last = bound - 1` for the loop `for (i in first until bound)`. - val bound = scope.createTemporaryVariable( - ensureNotNullable( - call.getValueArgument(0)!!.castIfNecessary( - data.elementType(context.irBuiltIns), - data.elementCastFunctionName - ) - ), nameHint = "bound", - origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE + val boundArg = call.getValueArgument(0)!! + val bound = ensureNotNullable( + boundArg.castIfNecessary( + data.elementType(context.irBuiltIns), + data.elementCastFunctionName + ) ) - val decFun = data.decFun(context.irBuiltIns) - val last = irCallOp(decFun.symbol, bound.type, irGet(bound)) + // `bound` may be needed for an additional condition to the emptiness check (see comments below). If so, store `bound` in a + // temporary variable as it may be an expression with side-effects and we should only evaluate it once. + val boundVar = if (needsMinValueCondition(data, boundArg)) scope.createTemporaryVariable( + bound, + nameHint = "bound", + origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE + ) else null - // The default "not empty" check cannot be used for the required for the corner case: + // `last = bound - 1` for the loop `for (i in first until bound)`. + val decFun = data.decFun(context.irBuiltIns) + val last = irCallOp(decFun.symbol, bound.type, if (boundVar != null) irGet(boundVar) else bound) + + // The default "not empty" check cannot be used for the corner case: // // for (i in a until MIN_VALUE) {} // // ...which should always be considered an empty range. When the given bound is MIN_VALUE, and because `last = bound - 1`, // "last" will underflow to MAX_VALUE, therefore the default "not empty" check: // - // if (first <= last) { /* loop */ } + // if (inductionVar <= last) { /* loop */ } // // ...will always be true and won't consider the range as empty. Therefore, we need to add an additional condition to the - // emptiness check so that it becomes: + // "not empty" check so that it becomes: // - // if (first <= last && bound > MIN_VALUE) { /* loop */ } + // if (inductionVar <= last && bound > MIN_VALUE) { /* loop */ } ProgressionHeaderInfo( data, first = call.extensionReceiver!!, last = last, step = irInt(1), - additionalVariables = listOf(bound), - additionalNotEmptyCondition = buildMinValueCondition(data, irGet(bound)) + canOverflow = false, + additionalVariables = listOfNotNull(boundVar), + additionalNotEmptyCondition = if (boundVar != null) buildMinValueCondition(data, irGet(boundVar)) else null ) } + /** Returns true (i.e., min value condition is needed) if `bound` is non-const OR is MIN_VALUE. */ + private fun needsMinValueCondition(progressionType: ProgressionType, bound: IrExpression): Boolean { + val boundValue = (bound as? IrConst<*>)?.value + val boundValueAsLong = when (boundValue) { + is Number -> boundValue.toLong() + is Char -> boundValue.toLong() + else -> return true // If "bound" is not a const Number or Char. + } + val minValueAsLong = when (progressionType) { + ProgressionType.INT_PROGRESSION -> Int.MIN_VALUE.toLong() + ProgressionType.CHAR_PROGRESSION -> Char.MIN_VALUE.toLong() + ProgressionType.LONG_PROGRESSION -> Long.MIN_VALUE + } + return minValueAsLong == boundValueAsLong + } + private fun DeclarationIrBuilder.buildMinValueCondition(progressionType: ProgressionType, bound: IrExpression): IrExpression { val irBuiltIns = context.irBuiltIns val minConst = when (progressionType) { @@ -154,7 +178,8 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa data, first = irInt(0), last = last, - step = irInt(1) + step = irInt(1), + canOverflow = false // Cannot overflow because `last` is at most MAX_VALUE - 1 ) } } diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt new file mode 100644 index 00000000000..2fe6aeb695a --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt @@ -0,0 +1,14 @@ +const val M = Char.MIN_VALUE + +fun f(a: Char) { + for (i in a downTo M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt new file mode 100644 index 00000000000..3817b24cb11 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt @@ -0,0 +1,14 @@ +const val M = Int.MIN_VALUE + +fun f(a: Int) { + for (i in a downTo M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt new file mode 100644 index 00000000000..168b3a7a18b --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt @@ -0,0 +1,15 @@ +const val M = Long.MIN_VALUE + +fun f(a: Long) { + for (i in a downTo M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 LCMP +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInObjectArrayIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInObjectArrayIndices.kt index 3f317b1b9a5..fc3aa3ceb3b 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInObjectArrayIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInObjectArrayIndices.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test() { var sum = 0 for (i in arrayOf("", "", "", "").indices) { @@ -12,6 +11,4 @@ fun test() { // 0 getFirst // 0 getLast -// 0 IF_ICMPGT -// 0 IF_ICMPEQ -// 1 IF_ICMPGE +// 1 IF_ICMP diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInPrimitiveArrayIndices.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInPrimitiveArrayIndices.kt index 49930a1ca4e..ad3f747ef55 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInPrimitiveArrayIndices.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInIndices/forInPrimitiveArrayIndices.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test() { var sum = 0 for (i in intArrayOf(0, 0, 0, 0).indices) { @@ -12,6 +11,4 @@ fun test() { // 0 getFirst // 0 getLast -// 0 IF_ICMPGT -// 0 IF_ICMPEQ -// 1 IF_ICMPGE +// 1 IF_ICMP diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt index e3c9ff700e1..6ed392dc8d9 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR const val N = 'Z' fun test(): Int { @@ -9,5 +8,10 @@ fun test(): Int { return sum } -// 0 IF_ICMPEQ -// 1 IF_ICMPGT \ No newline at end of file +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt new file mode 100644 index 00000000000..7843d76ef46 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt @@ -0,0 +1,14 @@ +const val M = Char.MAX_VALUE + +fun f(a: Char) { + for (i in a..M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt index 5db5d9334c2..826e085c5c5 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR const val N = 42 fun test(): Int { @@ -9,5 +8,10 @@ fun test(): Int { return sum } -// 0 IF_ICMPEQ -// 1 IF_ICMPGT \ No newline at end of file +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt new file mode 100644 index 00000000000..dd8518da46e --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt @@ -0,0 +1,14 @@ +const val M = Int.MAX_VALUE + +fun f(a: Int) { + for (i in a..M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt index d8a392d932b..fdfd915eef2 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR const val N = 42L fun test(): Long { @@ -9,6 +8,14 @@ fun test(): Long { return sum } +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep // 1 LCMP // 0 IFEQ -// 1 IFGT \ No newline at end of file +// 1 IFGT +// 0 L2I +// 0 I2L \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt new file mode 100644 index 00000000000..ff3fb3f5510 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt @@ -0,0 +1,15 @@ +const val M = Long.MAX_VALUE + +fun f(a: Long) { + for (i in a..M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 2 LCMP +// 2 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt index 57c56df9b0c..522fb251a7d 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR object Host { const val M = 1 const val N = 4 @@ -12,5 +11,10 @@ fun test(): Int { return s } -// 0 IF_ICMPEQ -// 1 IF_ICMPGT \ No newline at end of file +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt index b2c13ac872e..bd08b8fc554 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt @@ -11,4 +11,5 @@ fun test(): Int { // 0 getEnd // 0 getFirst // 0 getLast -// 0 getStep \ No newline at end of file +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt new file mode 100644 index 00000000000..a17c6d78008 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt @@ -0,0 +1,14 @@ +const val M = Char.MAX_VALUE + +fun f(a: Char) { + for (i in a until M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt new file mode 100644 index 00000000000..413ea5c0afd --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt @@ -0,0 +1,14 @@ +const val M = Int.MAX_VALUE + +fun f(a: Int) { + for (i in a until M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt new file mode 100644 index 00000000000..2a5c5f15db5 --- /dev/null +++ b/compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt @@ -0,0 +1,15 @@ +const val M = Long.MAX_VALUE + +fun f(a: Long) { + for (i in a until M) { + } +} + +// 0 iterator +// 0 getStart +// 0 getEnd +// 0 getFirst +// 0 getLast +// 0 getStep +// 1 LCMP +// 1 IF \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt b/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt index 3f5a2616da4..eddeb534ade 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt @@ -1,4 +1,3 @@ -// IGNORE_BACKEND: JVM_IR fun test(): Int { var sum = 0 for (i in 4 downTo 1) { @@ -12,5 +11,4 @@ fun test(): Int { // 0 getEnd // 0 getFirst // 0 getLast -// 0 IF_ICMPEQ -// 1 IF_ICMPLT +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange1.kt b/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange1.kt index fc049d3ecd7..a1b8812cfa6 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange1.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange1.kt @@ -8,4 +8,5 @@ fun f() { // 0 getEnd // 0 getFirst // 0 getLast -// 0 getStep \ No newline at end of file +// 0 getStep +// 1 IF_ICMP \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange2.kt b/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange2.kt index 00580934a1b..c3c20c6f4c2 100644 --- a/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange2.kt +++ b/compiler/testData/codegen/bytecodeText/forLoop/primitiveLiteralRange2.kt @@ -8,4 +8,5 @@ fun f(a: Int, b: Int) { // 0 getEnd // 0 getFirst // 0 getLast -// 0 getStep \ No newline at end of file +// 0 getStep +// 2 IF_ICMP \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java index b3b81e1f469..0e7dc1d0bad 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BytecodeTextTestGenerated.java @@ -1627,6 +1627,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInCharSequence.kt"); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt"); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt"); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt"); + } + @TestMetadata("forInObjectArray.kt") public void testForInObjectArray() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInObjectArray.kt"); @@ -1652,16 +1667,31 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt"); } + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt"); + } + @TestMetadata("forInRangeToConst.kt") public void testForInRangeToConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt"); } + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt"); + } + @TestMetadata("forInRangeToLongConst.kt") public void testForInRangeToLongConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt"); } + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt"); + } + @TestMetadata("forInRangeToQualifiedConst.kt") public void testForInRangeToQualifiedConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt"); @@ -1682,6 +1712,21 @@ public class BytecodeTextTestGenerated extends AbstractBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt"); } + @TestMetadata("forInUntilCharMaxValue.kt") + public void testForInUntilCharMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt"); + } + + @TestMetadata("forInUntilIntMaxValue.kt") + public void testForInUntilIntMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt"); + } + + @TestMetadata("forInUntilLongMaxValue.kt") + public void testForInUntilLongMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java index a1291beace7..0aa2ef539e9 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBytecodeTextTestGenerated.java @@ -1637,6 +1637,21 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInCharSequence.kt"); } + @TestMetadata("forInDownToCharMinValue.kt") + public void testForInDownToCharMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToCharMinValue.kt"); + } + + @TestMetadata("forInDownToIntMinValue.kt") + public void testForInDownToIntMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToIntMinValue.kt"); + } + + @TestMetadata("forInDownToLongMinValue.kt") + public void testForInDownToLongMinValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInDownToLongMinValue.kt"); + } + @TestMetadata("forInObjectArray.kt") public void testForInObjectArray() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInObjectArray.kt"); @@ -1662,16 +1677,31 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharConst.kt"); } + @TestMetadata("forInRangeToCharMaxValue.kt") + public void testForInRangeToCharMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToCharMaxValue.kt"); + } + @TestMetadata("forInRangeToConst.kt") public void testForInRangeToConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt"); } + @TestMetadata("forInRangeToIntMaxValue.kt") + public void testForInRangeToIntMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToIntMaxValue.kt"); + } + @TestMetadata("forInRangeToLongConst.kt") public void testForInRangeToLongConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt"); } + @TestMetadata("forInRangeToLongMaxValue.kt") + public void testForInRangeToLongMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongMaxValue.kt"); + } + @TestMetadata("forInRangeToQualifiedConst.kt") public void testForInRangeToQualifiedConst() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt"); @@ -1692,6 +1722,21 @@ public class IrBytecodeTextTestGenerated extends AbstractIrBytecodeTextTest { runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntil.kt"); } + @TestMetadata("forInUntilCharMaxValue.kt") + public void testForInUntilCharMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilCharMaxValue.kt"); + } + + @TestMetadata("forInUntilIntMaxValue.kt") + public void testForInUntilIntMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilIntMaxValue.kt"); + } + + @TestMetadata("forInUntilLongMaxValue.kt") + public void testForInUntilLongMaxValue() throws Exception { + runTest("compiler/testData/codegen/bytecodeText/forLoop/forInUntilLongMaxValue.kt"); + } + @TestMetadata("forIntInDownTo.kt") public void testForIntInDownTo() throws Exception { runTest("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt");