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.getClass
|
||||||
import org.jetbrains.kotlin.ir.types.toKotlinType
|
import org.jetbrains.kotlin.ir.types.toKotlinType
|
||||||
import org.jetbrains.kotlin.ir.util.functions
|
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.IrElementTransformerVoid
|
||||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||||
|
|
||||||
@@ -39,41 +40,50 @@ val forLoopsPhase = makeIrFilePhase(
|
|||||||
* a simple while loop over primitive induction variable.
|
* a simple while loop over primitive induction variable.
|
||||||
*
|
*
|
||||||
* For example, this loop:
|
* 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:
|
* 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()
|
* // 1. If the induction variable cannot overflow, i.e., `last` is const and != MAX_VALUE (if increasing, or MIN_VALUE if decreasing),
|
||||||
* while (it.hasNext()) {
|
* // OR if loop is an until loop (e.g., `for (i in first until last)`):
|
||||||
* val i = it.next()
|
|
||||||
* ...
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
*
|
||||||
* 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
|
||||||
|
* }
|
||||||
*
|
*
|
||||||
* ```
|
* // 2. If the induction variable CAN overflow, i.e., `last` is not const or is MAX/MIN_VALUE:
|
||||||
* 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:
|
|
||||||
*
|
*
|
||||||
|
* 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) {
|
* In case of iteration over an array, we transform it into the following:
|
||||||
* val i = array[it]
|
* ```
|
||||||
* it++
|
* 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 {
|
internal class ForLoopsLowering(val context: CommonBackendContext) : FileLoweringPass {
|
||||||
@@ -202,17 +212,13 @@ private class RangeLoopTransformer(
|
|||||||
|
|
||||||
// The "next" statement (at the top of the loop):
|
// The "next" statement (at the top of the loop):
|
||||||
//
|
//
|
||||||
// ```
|
// val i = it.next()
|
||||||
// val i = it.next()
|
|
||||||
// ```
|
|
||||||
//
|
//
|
||||||
// ...is lowered into:
|
// ...is lowered into:
|
||||||
//
|
//
|
||||||
// ```
|
// val i = initializeLoopVariable() // `inductionVariable` for progressions
|
||||||
// val i = initialValue() // `inductionVariable` for progressions
|
// // `array[inductionVariable]` for arrays
|
||||||
// // `array[inductionVariable]` for arrays
|
// inductionVariable = inductionVariable + step
|
||||||
// inductionVariable = inductionVariable + step
|
|
||||||
// ```
|
|
||||||
return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) {
|
return with(context.createIrBuilder(getScopeOwnerSymbol(), initializer.startOffset, initializer.endOffset)) {
|
||||||
variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this)
|
variable.initializer = forLoopInfo.initializeLoopVariable(symbols, this)
|
||||||
val plusFun = forLoopInfo.inductionVariable.type.getClass()!!.functions.first {
|
val plusFun = forLoopInfo.inductionVariable.type.getClass()!!.functions.first {
|
||||||
|
|||||||
+35
-2
@@ -65,7 +65,7 @@ internal sealed class HeaderInfo(
|
|||||||
) {
|
) {
|
||||||
val direction: ProgressionDirection by lazy {
|
val direction: ProgressionDirection by lazy {
|
||||||
// If step is a constant (either Int or Long), then we can determine the direction.
|
// 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()
|
val stepValueAsLong = stepValue?.toLong()
|
||||||
when {
|
when {
|
||||||
stepValueAsLong == null -> ProgressionDirection.UNKNOWN
|
stepValueAsLong == null -> ProgressionDirection.UNKNOWN
|
||||||
@@ -82,9 +82,42 @@ internal class ProgressionHeaderInfo(
|
|||||||
first: IrExpression,
|
first: IrExpression,
|
||||||
last: IrExpression,
|
last: IrExpression,
|
||||||
step: IrExpression,
|
step: IrExpression,
|
||||||
|
canOverflow: Boolean? = null,
|
||||||
val additionalVariables: List<IrVariable> = listOf(),
|
val additionalVariables: List<IrVariable> = listOf(),
|
||||||
val additionalNotEmptyCondition: IrExpression? = null
|
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. */
|
/** Information about a for-loop over an array. The internal induction variable used is an Int. */
|
||||||
internal class ArrayHeaderInfo(
|
internal class ArrayHeaderInfo(
|
||||||
|
|||||||
+27
-15
@@ -66,24 +66,36 @@ internal class ProgressionLoopHeader(
|
|||||||
get() = headerInfo.additionalVariables + listOf(inductionVariable, last, step)
|
get() = headerInfo.additionalVariables + listOf(inductionVariable, last, step)
|
||||||
|
|
||||||
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
||||||
// Condition: loopVariable != last
|
if (headerInfo.canOverflow) {
|
||||||
assert(loopVariable != null)
|
// Condition: loopVariable != last. We cannot use the induction variable because it can overflow.
|
||||||
val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" }
|
assert(loopVariable != null)
|
||||||
val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply {
|
val booleanNotFun = context.irBuiltIns.booleanClass.functions.first { it.owner.name.asString() == "not" }
|
||||||
putValueArgument(0, irGet(loopVariable!!))
|
val newCondition = irCallOp(booleanNotFun, booleanNotFun.owner.returnType, irCall(context.irBuiltIns.eqeqSymbol).apply {
|
||||||
putValueArgument(1, irGet(last))
|
putValueArgument(0, irGet(loopVariable!!))
|
||||||
})
|
putValueArgument(1, irGet(last))
|
||||||
|
})
|
||||||
|
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
||||||
// TODO: Build while loop (instead of do-while) where possible, e.g., in "until" ranges, or when "last" is known to be < MAX_VALUE
|
label = loop.label
|
||||||
IrDoWhileLoopImpl(loop.startOffset, loop.endOffset, loop.type, loop.origin).apply {
|
condition = newCondition
|
||||||
label = loop.label
|
body = newBody
|
||||||
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? =
|
override fun buildNotEmptyConditionIfNecessary(builder: DeclarationIrBuilder): IrExpression? =
|
||||||
|
if (headerInfo.canOverflow) buildNotEmptyCondition(builder) else null
|
||||||
|
|
||||||
|
private fun buildNotEmptyCondition(builder: DeclarationIrBuilder): IrExpression =
|
||||||
with(builder) {
|
with(builder) {
|
||||||
val builtIns = context.irBuiltIns
|
val builtIns = context.irBuiltIns
|
||||||
val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType()
|
val progressionKotlinType = progressionType.elementType(builtIns).toKotlinType()
|
||||||
@@ -158,7 +170,7 @@ internal class ArrayLoopHeader(
|
|||||||
get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step)
|
get() = listOf(headerInfo.arrayVariable, inductionVariable, last, step)
|
||||||
|
|
||||||
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
override fun buildInnerLoop(builder: DeclarationIrBuilder, loop: IrLoop, newBody: IrExpression?): IrLoop = with(builder) {
|
||||||
// Condition: loopVariable != last
|
// Condition: inductionVariable != last
|
||||||
val builtIns = context.irBuiltIns
|
val builtIns = context.irBuiltIns
|
||||||
val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]!!
|
val callee = builtIns.lessOrEqualFunByOperandType[builtIns.int]!!
|
||||||
val newCondition = irCall(callee).apply {
|
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.declarations.IrFunction
|
||||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
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.IrExpression
|
||||||
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin
|
||||||
import org.jetbrains.kotlin.ir.types.getClass
|
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? =
|
override fun build(call: IrCall, data: ProgressionType): HeaderInfo? =
|
||||||
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
|
with(context.createIrBuilder(call.symbol, call.startOffset, call.endOffset)) {
|
||||||
// `last = bound - 1` for the loop `for (i in first until bound)`.
|
val boundArg = call.getValueArgument(0)!!
|
||||||
val bound = scope.createTemporaryVariable(
|
val bound = ensureNotNullable(
|
||||||
ensureNotNullable(
|
boundArg.castIfNecessary(
|
||||||
call.getValueArgument(0)!!.castIfNecessary(
|
data.elementType(context.irBuiltIns),
|
||||||
data.elementType(context.irBuiltIns),
|
data.elementCastFunctionName
|
||||||
data.elementCastFunctionName
|
)
|
||||||
)
|
|
||||||
), nameHint = "bound",
|
|
||||||
origin = IrDeclarationOrigin.FOR_LOOP_IMPLICIT_VARIABLE
|
|
||||||
)
|
)
|
||||||
val decFun = data.decFun(context.irBuiltIns)
|
// `bound` may be needed for an additional condition to the emptiness check (see comments below). If so, store `bound` in a
|
||||||
val last = irCallOp(decFun.symbol, bound.type, irGet(bound))
|
// 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) {}
|
// 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`,
|
// ...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:
|
// "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
|
// ...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(
|
ProgressionHeaderInfo(
|
||||||
data,
|
data,
|
||||||
first = call.extensionReceiver!!,
|
first = call.extensionReceiver!!,
|
||||||
last = last,
|
last = last,
|
||||||
step = irInt(1),
|
step = irInt(1),
|
||||||
additionalVariables = listOf(bound),
|
canOverflow = false,
|
||||||
additionalNotEmptyCondition = buildMinValueCondition(data, irGet(bound))
|
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 {
|
private fun DeclarationIrBuilder.buildMinValueCondition(progressionType: ProgressionType, bound: IrExpression): IrExpression {
|
||||||
val irBuiltIns = context.irBuiltIns
|
val irBuiltIns = context.irBuiltIns
|
||||||
val minConst = when (progressionType) {
|
val minConst = when (progressionType) {
|
||||||
@@ -154,7 +178,8 @@ internal class IndicesHandler(val context: CommonBackendContext) : ProgressionHa
|
|||||||
data,
|
data,
|
||||||
first = irInt(0),
|
first = irInt(0),
|
||||||
last = last,
|
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() {
|
fun test() {
|
||||||
var sum = 0
|
var sum = 0
|
||||||
for (i in arrayOf("", "", "", "").indices) {
|
for (i in arrayOf("", "", "", "").indices) {
|
||||||
@@ -12,6 +11,4 @@ fun test() {
|
|||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 0 getLast
|
||||||
|
|
||||||
// 0 IF_ICMPGT
|
// 1 IF_ICMP
|
||||||
// 0 IF_ICMPEQ
|
|
||||||
// 1 IF_ICMPGE
|
|
||||||
|
|||||||
+1
-4
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
fun test() {
|
fun test() {
|
||||||
var sum = 0
|
var sum = 0
|
||||||
for (i in intArrayOf(0, 0, 0, 0).indices) {
|
for (i in intArrayOf(0, 0, 0, 0).indices) {
|
||||||
@@ -12,6 +11,4 @@ fun test() {
|
|||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 0 getLast
|
||||||
|
|
||||||
// 0 IF_ICMPGT
|
// 1 IF_ICMP
|
||||||
// 0 IF_ICMPEQ
|
|
||||||
// 1 IF_ICMPGE
|
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
// IGNORE_BACKEND: JVM_IR
|
|
||||||
const val N = 'Z'
|
const val N = 'Z'
|
||||||
|
|
||||||
fun test(): Int {
|
fun test(): Int {
|
||||||
@@ -9,5 +8,10 @@ fun test(): Int {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 IF_ICMPEQ
|
// 0 iterator
|
||||||
// 1 IF_ICMPGT
|
// 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
|
const val N = 42
|
||||||
|
|
||||||
fun test(): Int {
|
fun test(): Int {
|
||||||
@@ -9,5 +8,10 @@ fun test(): Int {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 IF_ICMPEQ
|
// 0 iterator
|
||||||
// 1 IF_ICMPGT
|
// 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
|
const val N = 42L
|
||||||
|
|
||||||
fun test(): Long {
|
fun test(): Long {
|
||||||
@@ -9,6 +8,14 @@ fun test(): Long {
|
|||||||
return sum
|
return sum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 0 iterator
|
||||||
|
// 0 getStart
|
||||||
|
// 0 getEnd
|
||||||
|
// 0 getFirst
|
||||||
|
// 0 getLast
|
||||||
|
// 0 getStep
|
||||||
// 1 LCMP
|
// 1 LCMP
|
||||||
// 0 IFEQ
|
// 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 {
|
object Host {
|
||||||
const val M = 1
|
const val M = 1
|
||||||
const val N = 4
|
const val N = 4
|
||||||
@@ -12,5 +11,10 @@ fun test(): Int {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
// 0 IF_ICMPEQ
|
// 0 iterator
|
||||||
// 1 IF_ICMPGT
|
// 0 getStart
|
||||||
|
// 0 getEnd
|
||||||
|
// 0 getFirst
|
||||||
|
// 0 getLast
|
||||||
|
// 0 getStep
|
||||||
|
// 1 IF_ICMP
|
||||||
@@ -11,4 +11,5 @@ fun test(): Int {
|
|||||||
// 0 getEnd
|
// 0 getEnd
|
||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 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 {
|
fun test(): Int {
|
||||||
var sum = 0
|
var sum = 0
|
||||||
for (i in 4 downTo 1) {
|
for (i in 4 downTo 1) {
|
||||||
@@ -12,5 +11,4 @@ fun test(): Int {
|
|||||||
// 0 getEnd
|
// 0 getEnd
|
||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 0 getLast
|
||||||
// 0 IF_ICMPEQ
|
// 1 IF_ICMP
|
||||||
// 1 IF_ICMPLT
|
|
||||||
@@ -8,4 +8,5 @@ fun f() {
|
|||||||
// 0 getEnd
|
// 0 getEnd
|
||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 0 getLast
|
||||||
// 0 getStep
|
// 0 getStep
|
||||||
|
// 1 IF_ICMP
|
||||||
@@ -8,4 +8,5 @@ fun f(a: Int, b: Int) {
|
|||||||
// 0 getEnd
|
// 0 getEnd
|
||||||
// 0 getFirst
|
// 0 getFirst
|
||||||
// 0 getLast
|
// 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");
|
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")
|
@TestMetadata("forInObjectArray.kt")
|
||||||
public void testForInObjectArray() throws Exception {
|
public void testForInObjectArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInObjectArray.kt");
|
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");
|
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")
|
@TestMetadata("forInRangeToConst.kt")
|
||||||
public void testForInRangeToConst() throws Exception {
|
public void testForInRangeToConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt");
|
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")
|
@TestMetadata("forInRangeToLongConst.kt")
|
||||||
public void testForInRangeToLongConst() throws Exception {
|
public void testForInRangeToLongConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt");
|
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")
|
@TestMetadata("forInRangeToQualifiedConst.kt")
|
||||||
public void testForInRangeToQualifiedConst() throws Exception {
|
public void testForInRangeToQualifiedConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt");
|
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");
|
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")
|
@TestMetadata("forIntInDownTo.kt")
|
||||||
public void testForIntInDownTo() throws Exception {
|
public void testForIntInDownTo() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt");
|
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");
|
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")
|
@TestMetadata("forInObjectArray.kt")
|
||||||
public void testForInObjectArray() throws Exception {
|
public void testForInObjectArray() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInObjectArray.kt");
|
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");
|
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")
|
@TestMetadata("forInRangeToConst.kt")
|
||||||
public void testForInRangeToConst() throws Exception {
|
public void testForInRangeToConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToConst.kt");
|
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")
|
@TestMetadata("forInRangeToLongConst.kt")
|
||||||
public void testForInRangeToLongConst() throws Exception {
|
public void testForInRangeToLongConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToLongConst.kt");
|
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")
|
@TestMetadata("forInRangeToQualifiedConst.kt")
|
||||||
public void testForInRangeToQualifiedConst() throws Exception {
|
public void testForInRangeToQualifiedConst() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forInRangeToQualifiedConst.kt");
|
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");
|
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")
|
@TestMetadata("forIntInDownTo.kt")
|
||||||
public void testForIntInDownTo() throws Exception {
|
public void testForIntInDownTo() throws Exception {
|
||||||
runTest("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt");
|
runTest("compiler/testData/codegen/bytecodeText/forLoop/forIntInDownTo.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user