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.
This commit is contained in:
committed by
max-kammerer
parent
ba0e016c4e
commit
7680e7fd56
+40
-34
@@ -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 {
|
||||
|
||||
+35
-2
@@ -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<IrVariable> = 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(
|
||||
|
||||
+27
-15
@@ -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 {
|
||||
|
||||
+43
-18
@@ -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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
+1
-4
@@ -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
|
||||
|
||||
+1
-4
@@ -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
|
||||
|
||||
@@ -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
|
||||
// 0 iterator
|
||||
// 0 getStart
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 1 IF_ICMP
|
||||
@@ -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
|
||||
@@ -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
|
||||
// 0 iterator
|
||||
// 0 getStart
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 1 IF_ICMP
|
||||
@@ -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
|
||||
@@ -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
|
||||
// 1 IFGT
|
||||
// 0 L2I
|
||||
// 0 I2L
|
||||
@@ -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
|
||||
+7
-3
@@ -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
|
||||
// 0 iterator
|
||||
// 0 getStart
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 1 IF_ICMP
|
||||
@@ -11,4 +11,5 @@ fun test(): Int {
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 0 getStep
|
||||
// 1 IF_ICMP
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -8,4 +8,5 @@ fun f() {
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 0 getStep
|
||||
// 1 IF_ICMP
|
||||
@@ -8,4 +8,5 @@ fun f(a: Int, b: Int) {
|
||||
// 0 getEnd
|
||||
// 0 getFirst
|
||||
// 0 getLast
|
||||
// 0 getStep
|
||||
// 0 getStep
|
||||
// 2 IF_ICMP
|
||||
@@ -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");
|
||||
|
||||
+45
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user